-- ===========================================================================
-- Diplomacy Trade View Manager
-- ===========================================================================
-- 此代码管理外交和贸易视图的显示和交互，包括玩家和AI之间的外交关系、贸易选项等。

include( "InstanceManager" ); -- 包含实例管理器
include( "SupportFunctions" ); -- 包含支持函数
include( "Civ6Common" ); -- 包含文明6的通用功能
include( "LeaderSupport" ); -- 包含领袖支持功能
include( "DiplomacyRibbonSupport" ); -- 包含外交横幅支持功能
include( "DiplomacyStatementSupport" ); -- 包含外交声明支持功能
include( "TeamSupport" ); -- 包含团队支持功能
include( "GameCapabilities" ); -- 包含游戏能力支持
include( "LeaderIcon" ); -- 包含领袖图标支持
include( "PopupDialog" ); -- 包含弹出对话框支持
include( "CivilizationIcon" ); -- 包含文明图标支持

-- ===========================================================================
--    全局
-- ===========================================================================

-- 实例管理器初始化
g_ActionListIM        = InstanceManager:new( "ActionButton",  "Button" );
g_SubActionListIM    = InstanceManager:new( "ActionButton",  "Button" );

-- 常量定义
MAX_BEFORE_TRUNC_BUTTON_INST        = 350;
COLOR_BLUE_GRAY                        = UI.GetColorValueFromHexLiteral(0xFF9c8772);
COLOR_BUTTONTEXT_SELECTED            = UI.GetColorValueFromHexLiteral(0xFF291F10);
COLOR_BUTTONTEXT_SELECTED_SHADOW    = UI.GetColorValueFromHexLiteral(0xAAD8B489);
COLOR_BUTTONTEXT_NORMAL                = UI.GetColorValueFromHexLiteral(0xFFC9DAE7);
COLOR_BUTTONTEXT_NORMAL_SHADOW        = UI.GetColorValueFromHexLiteral(0xA291F10);
COLOR_BUTTONTEXT_DISABLED            = UI.GetColorValueFromHexLiteral(0xFF90999F);

-- 当前是否为本地玩家的回合
g_bIsLocalPlayerTurn = true;

-- ===========================================================================
--    常量
-- ===========================================================================
-- 各种模式的常量定义
local LEADERTEXT_PADDING_X        :number        = 40;
local LEADERTEXT_PADDING_Y        :number        = 40;
local SELECTION_PADDING_Y        :number        = 20;
-- 例如概览模式、对话模式、影院模式和交易模式
local OVERVIEW_MODE = 0;
local CONVERSATION_MODE = 1;
local CINEMA_MODE = 2;
local DEAL_MODE = 3;

-- 图标大小和面板定义
local SIZE_BUILDING_ICON    :number = 32;
local SIZE_UNIT_ICON        :number = 32;

-- 情报面板的相关常量
local INTEL_NO_SUB_PANEL            = -1;
local INTEL_ACCESS_LEVEL_PANEL        = 0;
local INTEL_RELATIONSHIP_PANEL        = 1;
local INTEL_GOSSIP_HISTORY_PANEL    = 2;
local INTEL_AGENDA_PANEL            = 3;

-- 外交横幅偏移和最大按钮实例数
local DIPLOMACY_RIBBON_OFFSET            = 64;
local MAX_BEFORE_TRUNC_BUTTON_INST        = 280;
local PADDING_FOR_SCROLLPANEL            = 220;

-- 团队横幅大小定义
local TEAM_RIBBON_SIZE                :number = 53;
local TEAM_RIBBON_SMALL_SIZE        :number = 30;
local TEAM_RIBBON_PREFIX            :string = "ICON_TEAM_RIBBON_";

-- 领袖语音支持
local VOICEOVER_SUPPORT: table = {"KUDOS", "WARNING", "DECLARE_WAR_FROM_HUMAN", "DECLARE_WAR_FROM_AI", "FIRST_MEET", "DEFEAT","ENRAGED"};

-- 对话控制覆盖屏幕的部分比例
local CONVO_X_MULTIPLIER    = .328;

-- ===========================================================================
--    变量
-- ===========================================================================

-- 玩家面板实例管理器
local ms_PlayerPanelIM        :table        = InstanceManager:new( "PlayerPanel",  "Root" );
-- 外交横幅实例管理器
local ms_DiplomacyRibbonIM    :table        = InstanceManager:new( "DiplomacyRibbonVert",  "Root" );
-- 领袖横幅实例管理器
local ms_DiplomacyRibbonLeaderIM    :table        = InstanceManager:new( "DiplomacyRibbonLeader",  "Root" );
-- 只有图标的实例管理器
local ms_IconOnlyIM            :table        = InstanceManager:new( "IconOnly",  "Icon");

-- 图标和文本的实例管理器
local ms_IconAndTextIM        :table        = InstanceManager:new( "IconAndText",  "SelectButton", Controls.IconAndTextContainer );
-- 左右列表的实例管理器
local ms_LeftRightListIM    :table        = InstanceManager:new( "LeftRightList",  "List", Controls.LeftRightListContainer );
-- 上下列表的实例管理器
local ms_TopDownListIM        :table        = InstanceManager:new( "TopDownList",  "List", Controls.TopDownListContainer );

-- 情报面板的实例管理器
local ms_IntelPanelIM                :table = InstanceManager:new( "IntelPanel",  "Panel" );
-- 情报标签按钮的实例管理器
local ms_IntelTabButtonIM            :table = InstanceManager:new( "IntelTabButtonInstance", "Button" );

-- 情报面板的各个实例
local ms_IntelOverviewIM            :table = InstanceManager:new( "IntelOverviewInstance", "Top" );
local ms_IntelGossipIM                :table = InstanceManager:new( "IntelGossipHistoryPanel", "Top" );
local ms_IntelAccessLevelIM            :table = InstanceManager:new( "IntelAccessLevelPanel", "Top" );
local ms_IntelRelationshipIM        :table = InstanceManager:new( "IntelRelationshipPanel", "Top" );
local ms_IntelTabAnchorIM            :table = InstanceManager:new( "IntelTabAnchorInstance", "Anchor" );

-- 情报概览行的实例管理器
local ms_IntelOverviewDividerIM                :table = InstanceManager:new( "IntelOverviewDividerInstance", "TheDivider" );
local ms_IntelOverviewGossipIM                :table = InstanceManager:new( "IntelOverviewGossipInstance", "Top" );
local ms_IntelOverviewAccessLevelIM            :table = InstanceManager:new( "IntelOverviewAccessLevelInstance", "Top" );
local ms_IntelOverviewGovernmentIM            :table = InstanceManager:new( "IntelOverviewGovernmentInstance", "Top" );
local ms_IntelOverviewAgendasIM                :table = InstanceManager:new( "IntelOverviewAgendasInstance", "Top" );
local ms_IntelOverviewAgendaEntryIM            :table = InstanceManager:new( "IntelOverviewAgendaEntryInstance", "Top" );
local ms_IntelOverviewAgreementsIM            :table = InstanceManager:new( "IntelOverviewAgreementsInstance", "Top" );
local ms_IntelOverviewOurRelationshipIM        :table = InstanceManager:new( "IntelOverviewOurRelationshipInstance", "Top" );
local ms_IntelOverviewOtherRelationshipsIM    :table = InstanceManager:new( "IntelOverviewOtherRelationshipsInstance", "Top" );
local ms_IntelOverviewAnchorIM                :table = InstanceManager:new( "IntelOverviewAnchorInstance", "Anchor" );

-- 关系图标实例管理器
local ms_IntelRelationshipReasonIM    :table    = InstanceManager:new( "IntelRelationshipReasonEntry",  "Background" );
local ms_RelationshipIconsIM        :table    = InstanceManager:new( "RelationshipIcon",  "Background" );

-- 情报历史面板条目实例管理器
local ms_IntelGossipHistoryPanelEntryIM    :table    = InstanceManager:new( "IntelGossipHistoryPanelEntry",  "Background" );

-- 对话选择实例管理器
local ms_ConversationSelectionIM :table        = InstanceManager:new( "ConversationSelectionInstance",  "SelectionButton", Controls.ConversationSelectionStack );

-- 唯一图标和文本实例管理器
local ms_uniqueIconIM :table    = InstanceManager:new("IconInfoInstance", "Top", Controls.FeaturesStack );
local ms_uniqueTextIM :table    = InstanceManager:new("TextInfoInstance", "Top", Controls.FeaturesStack );

-- 玩家标识
local OTHER_PLAYER = 0;
local LOCAL_PLAYER = 1;

-- 玩家面板和外交横幅的局部变量
local ms_PlayerPanel =        nil;
ms_DiplomacyRibbon =    nil;

-- 当前本地玩家领袖ID
local ms_LocalPlayerLeaderID = -1;

-- 另一个可能与本地玩家联系的玩家，此变量可以为nil
local ms_OtherPlayer =        nil;
local ms_OtherPlayerID =    -1;

-- 当前选择的玩家领袖类型名称
local ms_SelectedPlayerLeaderTypeName = nil;

-- 正在显示的领袖名称
local ms_showingLeaderName = "";
-- 是否已请求显示领袖名称
local ms_bLeaderShowRequested = false;

-- 领袖ID到横幅条目的映射
local ms_LeaderIDToRibbonEntry = {};    
-- 由玩家发起的会话ID
local ms_InitiatedByPlayerID = -1;
-- 是否已初始化视图
local ms_bIsViewInitialized = false;
-- 当前视图模式
local ms_currentViewMode = -1;
-- 是否显示交易
local ms_bShowingDeal = false;
-- 是否处于热加载中
local m_isInHotload = false;
-- 是否在淡出完成后关闭会话
local m_bCloseSessionOnFadeComplete = false;
-- 事件ID
local m_eventID:number = 0;
-- 是否是第一次打开
local m_firstOpened = true;
-- 领袖坐标
local m_LeaderCoordinates        :table = {};
-- 上次为领袖播放音乐的时间
local m_lastLeaderPlayedMusicFor : number = -1;
-- 当前播放的mod音乐
local m_curModderMusic : number = -1;
-- 当前音乐是否来自mod
local m_bCurrentMusicIsModder : boolean = false;
-- 上次交易回应动画
local ms_LastDealResponseAnimation = nil;

-- 语音支持相关变量
local m_voiceoverText        :string = ""; -- 用于存储语音文本
local m_cinemaMode            :boolean = false; -- 电影院模式标志
local m_currentLeaderAnim    :string = ""; -- 当前领导动画名称
local m_currentSceneEffect    :string = ""; -- 当前场景效果名称

local ms_OtherID; -- 存储其他ID，用途未指定

local m_bIsModPaused : boolean = false; -- 模式是否暂停的标志

m_LiteMode = false; -- 简化模式标志，初始为false


-- ===========================================================================
-- 全局变量（在包含此文件的脚本中可访问）
-- ===========================================================================
ms_IntelPanel = nil; -- 情报面板
ms_LocalPlayer = nil; -- 本地玩家
ms_LocalPlayerID = -1; -- 本地玩家ID
-- 已选择的玩家。这可以是任何玩家，包括本地玩家
ms_SelectedPlayerID = -1;
ms_SelectedPlayer = nil;
ms_ActiveSessionID = nil; -- 当前活跃会话ID
m_bottomPanelHeight = 0; -- 底部面板高度

m_PopupDialog = PopupDialog:new("DiplomacyActionViewPopup"); -- 弹出对话框对象

-- ===========================================================================
-- 获取另一个玩家
-- 参数：
--   player : table - 输入的玩家表格
-- 返回值：
--   如果输入玩家是另一个玩家（ms_OtherPlayer），则返回本地玩家（ms_LocalPlayer）；否则返回另一个玩家（ms_OtherPlayer）。
function GetOtherPlayer(player : table)
    if (player ~= nil and player:GetID() == ms_OtherPlayer:GetID()) then
        return ms_LocalPlayer;
    end

    return ms_OtherPlayer;
end

-- ===========================================================================
-- 获取声明情绪
-- 参数：
--   fromPlayer : number - 来源玩家ID
--   inputMood : number - 输入的情绪类型
-- 返回值：
--   如果声明中没有定义情绪，返回当前情绪；否则返回输入的情绪。
function GetStatementMood( fromPlayer : number, inputMood : number )

    local pPlayer = Players[fromPlayer];
    local otherPlayerID = GetOtherPlayer(pPlayer):GetID();

    local eFromPlayerMood = inputMood;
    if (inputMood == DiplomacyMoodTypes.UNDEFINED) then
        -- 如果声明中未定义情绪，获取当前情绪。这在声明发送时常见，因为那时导致声明的情绪行动通常还未生效，所以当时的情绪不正确。
        return DiplomacySupport_GetPlayerMood(pPlayer, otherPlayerID);
    else
        return inputMood;
    end
end

local DiplomaticStateIndexToVisState = {}; -- 将外交状态索引转换为可视状态索引的映射

DiplomaticStateIndexToVisState[DiplomaticStates.ALLIED] = 0; -- 盟友关系
DiplomaticStateIndexToVisState[DiplomaticStates.DECLARED_FRIEND] = 1; -- 宣布为朋友
DiplomaticStateIndexToVisState[DiplomaticStates.FRIENDLY] = 2; -- 友好关系
DiplomaticStateIndexToVisState[DiplomaticStates.NEUTRAL] = 3; -- 中立关系
DiplomaticStateIndexToVisState[DiplomaticStates.UNFRIENDLY] = 4; -- 不友好关系
DiplomaticStateIndexToVisState[DiplomaticStates.DENOUNCED] = 5; -- 谴责关系
DiplomaticStateIndexToVisState[DiplomaticStates.WAR] = 6; -- 战争状态

-- ===========================================================================
-- 将外交状态索引转换为可视状态索引
-- 参数：
--   iState : 外交状态索引
-- 返回值：
--   可视状态索引
function GetVisStateFromDiplomaticState(iState)

    local eStateHash = GameInfo.DiplomaticStates[iState].Hash;
    local iVisState = DiplomaticStateIndexToVisState[eStateHash];

    if (iVisState ~= nil) then
        return iVisState;
    end

    return 0;
end

-- ===========================================================================
-- 更新已选择的玩家
-- 参数：
--   allowDeadPlayer - 是否允许选择已淘汰的玩家，默认为false
function UpdateSelectedPlayer(allowDeadPlayer)

    if (allowDeadPlayer == nil) then
        allowDeadPlayer = false;
    end

    -- 如果我们已见过他们且他们处于活跃状态（或允许选择已淘汰玩家），则选择他们
    if (ms_LocalPlayer:GetDiplomacy():HasMet(ms_SelectedPlayerID) and (allowDeadPlayer == true or ms_LeaderIDToRibbonEntry[ms_SelectedPlayerID] ~= nil)) then
        ms_SelectedPlayer = Players[ms_SelectedPlayerID];
    else
        ms_SelectedPlayer = ms_LocalPlayer;
        ms_SelectedPlayerID = ms_LocalPlayerID;
    end
    
    if (ms_SelectedPlayer ~= nil) then
        local playerConfig = PlayerConfigurations[ms_SelectedPlayer:GetID()];
        if (playerConfig ~= nil) then
            ms_SelectedPlayerLeaderTypeName = playerConfig:GetLeaderTypeName();
            ms_OtherCivilizationID = playerConfig:GetCivilizationTypeID();
            ms_OtherLeaderID = playerConfig:GetLeaderTypeID();
            ms_OtherID = ms_SelectedPlayer:GetID();
            ResetPlayerPanel(); -- 重置玩家面板
        end
    end

end

-- ===========================================================================
-- 在水平方向创建一个组
-- 参数：
--   rootStack : table - 根堆栈
--   title : string - 标题
-- 返回值：
--   创建的图标列表对象
function CreateHorizontalGroup(rootStack : table, title : string)
    local iconList = ms_LeftRightListIM:GetInstance(rootStack);
    if (title == nil or title == "") then
        iconList.Title:SetHide(true);        -- 隐藏标题
    else
        iconList.TitleText:LocalizeAndSetText(title); -- 设置标题文本
    end

    return iconList;
end

-- ===========================================================================
-- 在垂直方向创建一个组
-- 参数：
--   rootStack : table - 根堆栈
--   title : string - 标题
-- 返回值：
--   创建的图标列表对象
function CreateVerticalGroup(rootStack : table, title : string)
    local iconList = ms_TopDownListIM:GetInstance(rootStack);
    if (title == nil or title == "") then
        iconList.Title:SetHide(true);        -- 隐藏标题
    else
        iconList.TitleText:LocalizeAndSetText(title); -- 设置标题文本
    end

    return iconList;
end


-- ===========================================================================
-- 创建玩家面板
-- 参数：
--   rootControl : table - 根控制对象
-- 返回值：
--   玩家面板对象
function CreatePlayerPanel(rootControl : table)

    local playerPanel = ms_PlayerPanelIM:GetInstance(rootControl);

    return playerPanel;
end

-- ===========================================================================
-- 创建外交横幅
-- 参数：
--   rootControl : table - 根控制对象
-- 返回值：
--   外交横幅对象
function CreateDiplomacyRibbon(rootControl : table)

    local diplomacyRibbon = ms_DiplomacyRibbonIM:GetInstance(rootControl);

    return diplomacyRibbon;
end

-- ===========================================================================
-- 创建各面板
-- 用于创建玩家面板和外交横幅
function CreatePanels()
    -- 创建玩家面板
    ms_PlayerPanel = CreatePlayerPanel(Controls.PlayerContainer);
    -- 创建外交横幅
    ms_DiplomacyRibbon = CreateDiplomacyRibbon(Controls.DiplomacyRibbonContainer);
end

-- ===========================================================================
-- 验证活跃会话是否仍然存在
-- 返回值：
--   如果会话仍然有效，返回true；否则返回false。
function ValidateActiveSession()

    if (ms_ActiveSessionID ~= nil) then
        if (not DiplomacyManager.IsSessionIDOpen(ms_ActiveSessionID)) then
            ms_ActiveSessionID = nil;
            return false;
        end
    end

    return true;
end

-- ===========================================================================
-- 退出对话模式
-- 参数：
--   bForced : boolean - 是否强制退出
function ExitConversationMode(bForced : boolean)

    if (ms_currentViewMode == CONVERSATION_MODE) then
        ValidateActiveSession();
        if (ms_ActiveSessionID ~= nil) then
            -- 关闭会话，这将处理返回OVERVIEW_MODE或如果对方领袖联系我们则退出的情况
            if (HasNextQueuedSession(ms_ActiveSessionID)) then
                -- 如果还有下一个会话，我们希望在屏幕变黑后再发送CloseSession
                m_bCloseSessionOnFadeComplete = true;
                StartFadeOut();
            else
                -- 现在关闭会话
                DiplomacyManager.CloseSession( ms_ActiveSessionID );
            end
        else
            -- 由于某种原因没有会话，直接返回
            if (bForced) then
                Close();
            else
                SelectPlayer(ms_OtherPlayerID, OVERVIEW_MODE);
            end
        end        
        ResetPlayerPanel();
    end
end

-- ===========================================================================
-- 开始淡出，一个每次动画结束后转场函数
function StartFadeOut()
    -- BlackFadeAnim一个黑屏转场动画，开始淡入/淡出
    Controls.BlackFade:SetHide(false);
    Controls.BlackFadeAnim:SetToBeginning();
    Controls.BlackFadeAnim:Play();
    -- FadeTimerAnim已经注册一个结束回调，以便在淡入/淡出动画结束时切换电影模式
    Controls.FadeTimerAnim:SetToBeginning();
    Controls.FadeTimerAnim:Play();
end

-- ===========================================================================
-- 开始淡入
function StartFadeIn()
    Controls.BlackFade:SetHide(false);

    -- 仅执行BlackFadeAnim
    Controls.BlackFadeAnim:SetToBeginning();    -- 这会强制清除反向标志
    Controls.BlackFadeAnim:SetToEnd();
    Controls.BlackFadeAnim:Reverse();
end

-- ===========================================================================
-- 判断是否为战争选项
-- 参数 key 字符串，代表某个选择键
--   返回值 布尔值，如果key代表的是某场战争的宣战选项，则返回true；否则返回false。
function IsWarChoice(key)
    local isWar :boolean = key == "CHOICE_DECLARE_SURPRISE_WAR" 
        or key == "CHOICE_DECLARE_FORMAL_WAR" 
        or key == "CHOICE_DECLARE_HOLY_WAR" 
        or key == "CHOICE_DECLARE_LIBERATION_WAR" 
        or key == "CHOICE_DECLARE_RECONQUEST_WAR"
        or key == "CHOICE_DECLARE_PROTECTORATE_WAR"
        or key == "CHOICE_DECLARE_COLONIAL_WAR"
        or key == "CHOICE_DECLARE_TERRITORIAL_WAR";
    return isWar;
end

-- 获取战争类型
-- 参数 key 字符串，代表某个宣战选项
--   返回值 整数，返回对应宣战选项的战争类型常量
function GetWarType(key)
        if (key == "CHOICE_DECLARE_FORMAL_WAR") then return WarTypes.FORMAL_WAR; end;
        if (key == "CHOICE_DECLARE_HOLY_WAR") then return WarTypes.HOLY_WAR; end;
        if (key == "CHOICE_DECLARE_LIBERATION_WAR") then return WarTypes.LIBERATION_WAR; end;
        if (key == "CHOICE_DECLARE_RECONQUEST_WAR") then return WarTypes.RECONQUEST_WAR; end;
        if (key == "CHOICE_DECLARE_PROTECTORATE_WAR") then return WarTypes.PROTECTORATE_WAR; end;
        if (key == "CHOICE_DECLARE_COLONIAL_WAR") then return WarTypes.COLONIAL_WAR; end;
        if (key == "CHOICE_DECLARE_TERRITORIAL_WAR") then return WarTypes.TERRITORIAL_WAR; end;

        return WarTypes.SURPRISE_WAR;
end

-- 获取动作的黄金成本
-- 参数 key 字符串，代表某个外交动作的键
--   返回值 整数，返回该外交动作所需的黄金成本；如果未找到对应动作，则返回0。
function GetGoldCost(key)

        local szActionString = ""; -- 用于存储对应动作的字符串标识

        if (key == "CHOICE_DIPLOMATIC_DELEGATION") then szActionString = "DIPLOACTION_DIPLOMATIC_DELEGATION"; end;
        if (key == "CHOICE_RESIDENT_EMBASSY") then szActionString = "DIPLOACTION_RESIDENT_EMBASSY"; end;
        if (key == "CHOICE_OPEN_BORDERS") then szActionString = "DIPLOACTION_OPEN_BORDERS"; end;

        if (szActionString == "") then return 0; end; -- 如果未找到对应的动作，则返回0

        return ms_LocalPlayer:GetDiplomacy():GetDiplomaticActionCost(szActionString); -- 返回动作的成本
end
-- ===========================================================================
-- 判断是否是和平选项
-- 参数 key 一个字符串，代表某个选择键
--   返回值 返回一个布尔值，如果key等于"CHOICE_MAKE_PEACE"，则返回true，否则返回false
function IsPeaceChoice(key)
    local isPeace :boolean = (key == "CHOICE_MAKE_PEACE");
    return isPeace;
end

-- 判断当前玩家是否可以发起外交声明
--   返回值 返回一个布尔值，如果当前玩家不是被选择的玩家，且被选择的玩家ID大于等于0，同时游戏未暂停，则返回true，否则返回false
function CanInitiateDiplomacyStatement()
    return ms_LocalPlayerID ~= ms_SelectedPlayerID and ms_SelectedPlayerID >= 0 and not GameConfiguration.IsPaused();
end

-- ===========================================================================
-- 处理从OVERVIEW_MODE选择的声明。此时我们还未与另一个玩家开始会话，这将开始一个会话。
function OnSelectInitialDiplomacyStatement(key)

    -- 检查是否可以发起外交声明
    if CanInitiateDiplomacyStatement() then

        -- 根据选择的键（例如宣战类型），发起不同的外交会话请求
        if (key == "CHOICE_DECLARE_SURPRISE_WAR") then 
            DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_SURPRISE_WAR");
            
        elseif (key == "CHOICE_DECLARE_FORMAL_WAR") then
            DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_FORMAL_WAR");
                
        elseif (key == "CHOICE_DECLARE_HOLY_WAR") then
            DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_HOLY_WAR");
                
        elseif (key == "CHOICE_DECLARE_LIBERATION_WAR") then
            DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_LIBERATION_WAR");
            
        elseif (key == "CHOICE_DECLARE_RECONQUEST_WAR") then
            DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_RECONQUEST_WAR");
            
        elseif (key == "CHOICE_DECLARE_PROTECTORATE_WAR") then
            DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_PROTECTORATE_WAR");
            
        elseif (key == "CHOICE_DECLARE_COLONIAL_WAR") then
            DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_COLONIAL_WAR");
            
        elseif (key == "CHOICE_DECLARE_TERRITORIAL_WAR") then
            DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_TERRITORIAL_WAR");
            
        -- 若选择的是和平选项，则清除待处理的协议，若无待处理协议，则创建一个空的和平协议
        elseif (key == "CHOICE_MAKE_PEACE") then
                -- 注释掉的代码块意在清除之前可能存在的和平协议，以确保用户能开始一个空白的和平协议谈判
                if (not DealManager.HasPendingDeal(ms_LocalPlayerID, ms_SelectedPlayerID)) then
                    DealManager.ClearWorkingDeal(DealDirection.OUTGOING, ms_LocalPlayerID, ms_SelectedPlayerID);
                    local pDeal = DealManager.GetWorkingDeal(DealDirection.OUTGOING, ms_LocalPlayer:GetID(), ms_SelectedPlayerID);
                    if (pDeal ~= nil) then
                        pDealItem = pDeal:AddItemOfType(DealItemTypes.AGREEMENTS, ms_LocalPlayer:GetID());
                        if (pDealItem ~= nil) then
                            pDealItem:SetSubType(DealAgreementTypes.MAKE_PEACE);
                            pDealItem:SetLocked(true);
                        end
                        -- 验证协议，确保双方都认同和平协议
                        pDeal:Validate();
                    end                            
                end
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "MAKE_DEAL");
        -- 若选择的是进行交易，则清除待处理的交易，以开始一个空白的交易谈判
        elseif (key == "CHOICE_MAKE_DEAL") then
                if (not DealManager.HasPendingDeal(ms_LocalPlayerID, ms_SelectedPlayerID)) then
                    DealManager.ClearWorkingDeal(DealDirection.OUTGOING, ms_LocalPlayerID, ms_SelectedPlayerID);
                end
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "MAKE_DEAL");
        -- 查看交易选项，会请求查看与选定玩家的待处理交易
        elseif (key == "CHOICE_VIEW_DEAL") then
                DealManager.ViewPendingDeal(ms_LocalPlayerID, ms_SelectedPlayerID);
        -- 若选择的是提出要求，则清除待处理的要求，以开始一个空白的要求谈判
        elseif (key == "CHOICE_MAKE_DEMAND") then
                if (not DealManager.HasPendingDeal(ms_LocalPlayerID, ms_SelectedPlayerID)) then
                    DealManager.ClearWorkingDeal(DealDirection.OUTGOING, ms_LocalPlayerID, ms_SelectedPlayerID);
                end
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "MAKE_DEMAND");
        -- 查看要求选项，会请求查看与选定玩家的待处理要求
        elseif (key == "CHOICE_VIEW_DEMAND") then
                DealManager.ViewPendingDeal(ms_LocalPlayerID, ms_SelectedPlayerID);
        -- 处理其余的外交选项，如谴责、外交代表团、宣布友好、常驻使馆、开放边界等
        elseif (key == "CHOICE_DENOUNCE") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DENOUNCE");
        elseif (key == "CHOICE_DIPLOMATIC_DELEGATION") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DIPLOMATIC_DELEGATION");
        elseif (key == "CHOICE_DECLARE_FRIENDSHIP") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "DECLARE_FRIEND");
        elseif (key == "CHOICE_RESIDENT_EMBASSY") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "RESIDENT_EMBASSY");
        elseif (key == "CHOICE_OPEN_BORDERS") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "OPEN_BORDERS");
        -- 处理关于禁止行为的要求，如禁止间谍活动、禁止在附近定居、禁止转化城市、禁止挖掘古迹等
        elseif (key == "CHOICE_DEMAND_PROMISE_DONT_SPY") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "WARNING_STOP_SPYING_ON_ME");
        elseif (key == "CHOICE_DEMAND_PROMISE_DONT_SETTLE_TOO_NEAR") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "WARNING_DONT_SETTLE_NEAR_ME");
        elseif (key == "CHOICE_DEMAND_PROMISE_DONT_CONVERT_CITY") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "WARNING_STOP_CONVERTING_MY_CITIES");
        elseif (key == "CHOICE_DEMAND_PROMISE_DONT_DIG_ARTIFACTS") then
                DiplomacyManager.RequestSession(ms_LocalPlayerID, ms_SelectedPlayerID, "WARNING_STOP_DIGGING_UP_ARTIFACTS");
        end
    end

end
-- ===========================================================================
-- 处理在CONVERSATION_MODE下的语句选择。此时我们已经与另一个玩家建立了一个会话。
function OnSelectConversationDiplomacyStatement(key)

    -- 如果选择退出会话
    if (key == "CHOICE_EXIT") then
        ExitConversationMode(false);
    else
        -- 处理宣战和和平等选项
        if (key == "CHOICE_DECLARE_SURPRISE_WAR") then 
            DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "DECLARE_SURPRISE_WAR");
            
        elseif (key == "CHOICE_DECLARE_FORMAL_WAR") then
            DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "DECLARE_FORMAL_WAR");
                
        elseif (key == "CHOICE_DECLARE_HOLY_WAR") then
            DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "DECLARE_HOLY_WAR");
                
        elseif (key == "CHOICE_DECLARE_LIBERATION_WAR") then
            DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "DECLARE_LIBERATION_WAR");
            
        elseif (key == "CHOICE_DECLARE_RECONQUEST_WAR") then
            DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "DECLARE_RECONQUEST_WAR");
            
        elseif (key == "CHOICE_DECLARE_PROTECTORATE_WAR") then
            DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "DECLARE_PROTECTORATE_WAR");
            
        elseif (key == "CHOICE_DECLARE_COLONIAL_WAR") then
            DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "DECLARE_COLONIAL_WAR");
            
        elseif (key == "CHOICE_DECLARE_TERRITORIAL_WAR") then
            DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "DECLARE_TERRITORIAL_WAR");
            
        -- 处理和平及其他外交选项
        elseif (key == "CHOICE_MAKE_PEACE") then
                DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "MAKE_PEACE");
        elseif (key == "CHOICE_MAKE_DEAL") then
                DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "MAKE_DEAL");
        elseif (key == "CHOICE_MAKE_DEMAND") then
                DiplomacyManager.AddStatement(ms_ActiveSessionID, Game.GetLocalPlayer(), "MAKE_DEMAND");
        else
            -- 处理积极、消极和忽略反馈
            if (key == "CHOICE_POSITIVE") then
                DiplomacyManager.AddResponse(ms_ActiveSessionID, Game.GetLocalPlayer(), "POSITIVE");
            else
                if (key == "CHOICE_NEGATIVE") then
                    DiplomacyManager.AddResponse(ms_ActiveSessionID, Game.GetLocalPlayer(), "NEGATIVE");
                else
                    if (key == "CHOICE_IGNORE") then
                        DiplomacyManager.AddResponse(ms_ActiveSessionID, Game.GetLocalPlayer(), "RESPONSE_IGNORE");
                    else
                        -- 检查是否选择了停止询问，并且对方玩家不是人类
                        if(key == "CHOICE_STOP_ASKING")then
                            -- 如果对方玩家是AI，进行确认对话框的设置和显示
                            if(not Players[ms_OtherPlayerID]:IsHuman())then
                                -- 获取对方玩家配置和文明名称
                                local kPlayerConfig : table = PlayerConfigurations[ms_OtherPlayerID];
                                local otherCivName : string = Locale.Lookup(kPlayerConfig:GetCivilizationShortDescription());
                                
                                -- 重置对话框并设置对话框内容
                                m_PopupDialog:Reset();
                                m_PopupDialog:AddText(Locale.Lookup("LOC_STOP_ASKING_POPUP_BODY", otherCivName));
                                
                                -- 添加取消按钮和确认（拒绝友谊）按钮
                                m_PopupDialog:AddButton(Locale.Lookup("LOC_CANCEL"), nil);
                                m_PopupDialog:AddButton(Locale.Lookup("LOC_UI_RELIGION_CONFIRM_RELIGION"), function() RejectFriendshipPermanent(); end, nil, nil, "PopupButtonInstanceRed");
                                
                                -- 显示对话框
                                m_PopupDialog:Open();
                            end
                        else
                            -- 将选择的关键字作为响应字符串直接传递
                            DiplomacyManager.AddResponse(ms_ActiveSessionID, Game.GetLocalPlayer(), key);
                        end
                    end
                end
            end
        end
    end
end

-- 拒绝友谊的回应
function RejectFriendshipPermanent()
    DiplomacyManager.AddResponse(ms_ActiveSessionID, Game.GetLocalPlayer(), "REJECTED_PERMANENT");
end
-- ===========================================================================
-- 应用当前语句到对话模式控件
function ApplyStatement(handler : table, statementTypeName : string, statementSubTypeName : string, toPlayer : number, kStatement : table)

    -- 获取发言人的语气，并解析语句
    local eFromPlayerMood = GetStatementMood( kStatement.FromPlayer,kStatement.FromPlayerMood);
    local kParsedStatement = handler.ExtractStatement(handler, statementTypeName, statementSubTypeName, kStatement.FromPlayer, eFromPlayerMood, kStatement.Initiator);
    handler.RemoveInvalidSelections(kParsedStatement, ms_LocalPlayerID, ms_OtherPlayerID);

    local leaderstr :string = "";
    local reasonStr :string = "";

    -- 设置领导者回应的文本
    if (kParsedStatement.StatementText ~= nil) then
        leaderstr = Locale.Lookup( DiplomacyManager.FindTextKey( kParsedStatement.StatementText, kStatement.FromPlayer, kStatement.FromMood, toPlayer));
        local reasonStrKey : string = DiplomacyManager.FindReasonTextKey( kParsedStatement.ReasonText, kStatement.FromPlayer, kStatement.AiReason, kStatement.AiModifier);
        if ( reasonStrKey ~= nil ) then
            reasonStr = Locale.Lookup( reasonStrKey );
            local agendaStr = DiplomacyManager.FindReasonAgendaTextKey(kStatement.FromPlayer, toPlayer, kStatement.AiReason, kStatement.AiModifier);
            if (agendaStr ~= nil ) then
                reasonStr = reasonStr .. agendaStr;
            end
        end
        Controls.LeaderResponseText:SetText( leaderstr );
        m_voiceoverText = leaderstr;
    end

    -- 重置对话选择实例
    ms_ConversationSelectionIM:ResetInstances();

    -- 遍历并显示可用的选择项
    if (kParsedStatement.Selections ~= nil) then
        for _, selection in ipairs(kParsedStatement.Selections) do
            if(selection.Key ~= "CHOICE_STOP_ASKING" or (selection.Key == "CHOICE_STOP_ASKING" and not Players[ms_OtherPlayerID]:IsHuman()))then
                local instance        :table    = ms_ConversationSelectionIM:GetInstance();
                instance.SelectionText:SetText( Locale.Lookup(selection.Text) );
            
                local texth            :number    = math.max( instance.SelectionText:GetSizeY() + SELECTION_PADDING_Y, 45 );
                instance.SelectionButton:SetSizeY( texth );
                instance.SelectionButton:SetToolTipString(); -- 清除可能存在的工具提示
                if (selection.IsDisabled == nil or selection.IsDisabled == false) then
                    instance.SelectionButton:SetDisabled( false );
                    instance.SelectionButton:RegisterCallback(Mouse.eMouseEnter, function() UI.PlaySound("Main_Menu_Mouse_Over"); end);
                    instance.SelectionButton:RegisterCallback( Mouse.eLClick, 
                        function() 
                            handler.OnSelectionButtonClicked(selection.Key); 
                        end );
                else
                    -- 设置禁用状态
                    instance.SelectionButton:SetDisabled( true );
                    if (selection.FailureReasons ~= nil) then
                        instance.SelectionButton:SetToolTipString(Locale.Lookup(selection.FailureReasons[1]));
                    end
                end
            end
        end
    end
    Controls.ConversationSelectionStack:CalculateSize();

    -- 更新领导者回应和原因文本
    Controls.LeaderResponseText:SetText( leaderstr );
    Controls.LeaderReasonText:SetText( reasonStr );

    -- 设置动画和场景效果
    m_currentLeaderAnim = kParsedStatement.LeaderAnimation;
    m_currentSceneEffect = kParsedStatement.SceneEffect;
    local ePlayerMood = DiplomacySupport_GetPlayerMood(ms_SelectedPlayer, ms_LocalPlayerID);
    print("ppkms_currentViewMode:", ms_currentViewMode)
    -- 根据当前视图模式更新领导者动画和场景效果
    if (ms_currentViewMode == CONVERSATION_MODE) then
        LeaderSupport_QueueAnimationSequence( ms_OtherLeaderName, kParsedStatement.LeaderAnimation, ePlayerMood );
        LeaderSupport_QueueSceneEffect( kParsedStatement.SceneEffect );
    elseif (ms_currentViewMode == DEAL_MODE) then        
        -- 根据玩家情绪设置不同闲置动画
        if (ePlayerMood == DiplomacyMoodTypes.HAPPY) then 
            LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "HAPPY_IDLE" );
        elseif (ePlayerMood == DiplomacyMoodTypes.NEUTRAL) then
            LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "NEUTRAL_IDLE" );
        elseif (ePlayerMood == DiplomacyMoodTypes.UNHAPPY) then
            LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "UNHAPPY_IDLE" );
        end
    end

    -- 更新领导者图标和名称
    local leaderIconController = CivilizationIcon:AttachInstance(Controls.LeaderResponseIcon);
    leaderIconController:UpdateIconFromPlayerID(kStatement.FromPlayer);

    local leaderDesc = PlayerConfigurations[kStatement.FromPlayer]:GetLeaderName();
    Controls.LeaderResponseName:SetText(Locale.ToUpper(Locale.Lookup("LOC_DIPLOMACY_DEAL_OTHER_PLAYER_SAYS", leaderDesc)));
end
-- ===========================================================================
-- 获取声明按钮的工具提示
-- 参数 pActionDef 动作定义，包含声明的详细信息
-- 返回 返回动作的描述信息，如果不存在则返回nil
function GetStatementButtonTooltip(pActionDef)
    if pActionDef and pActionDef.Description then -- 确保所有信息完整，描述是可选的！
        return Locale.Lookup(pActionDef.Description);
    end
    return nil;
end
-- ===========================================================================
-- 初始化声明列表
-- 参数 options 表格，包含列表项的各种选项
-- 参数 rootControl 表格，根控制元素
-- 参数 isSubList 布尔值，表示是否为子列表
function PopulateStatementList( options: table, rootControl: table, isSubList: boolean )
    local buttonIM:table;
    local stackControl:table;
    local selectionText :string = "[SIZE_16]";    -- 重置字符串大小以适应新的按钮实例
    if (isSubList) then
        buttonIM = g_ActionListIM;
        stackControl = rootControl.SubOptionStack;        
    else
        buttonIM = g_SubActionListIM;
        stackControl = rootControl.OptionStack;
    end
    buttonIM:ResetInstances();

    for _, selection in ipairs(options) do
        local instance        :table        = buttonIM:GetInstance(stackControl);
        local selectionText :string        = selectionText.. Locale.Lookup(selection.Text);
        local callback        :ifunction;
        local tooltipString    :string        = nil;
        if( selection.Key ~= nil) then
            callback    = function() OnSelectInitialDiplomacyStatement( selection.Key ) end;

            -- 设置按钮的工具提示
            local pActionDef = GameInfo.DiplomaticActions[selection.DiplomaticActionType];
            instance.Button:SetToolTipString(GetStatementButtonTooltip(pActionDef));

            -- 如果需要花费黄金，则添加文本
            local iCost = GetGoldCost(selection.Key);
            if iCost > 0 then
                local szGoldString = Locale.Lookup("LOC_DIPLO_CHOICE_GOLD_INFO", iCost);
                selectionText = selectionText .. szGoldString; 
            end

            -- 如果是战争声明，则添加战争惩罚信息
            if (IsWarChoice(selection.Key))then
                local eWarType = GetWarType(selection.Key);
                local iWarmongerPoints = ms_LocalPlayer:GetDiplomacy():ComputeDOWWarmongerPoints(ms_SelectedPlayerID, eWarType);
                local szWarmongerLevel = ms_LocalPlayer:GetDiplomacy():GetWarmongerLevel(-iWarmongerPoints);
                local szWarmongerString = Locale.Lookup("LOC_DIPLO_CHOICE_WARMONGER_INFO", szWarmongerLevel);
                selectionText = selectionText .. szWarmongerString; 

                -- 改变回调函数，先提示确认对话框
                callback = function() 
                    LuaEvents.DiplomacyActionView_ConfirmWarDialog(ms_LocalPlayerID, ms_SelectedPlayerID, eWarType);
                end;
            end
            
            -- 如果是谴责声明，则改变回调函数，先显示确认对话框
            if (selection.Key == "CHOICE_DENOUNCE")then
                local denounceFn = function() OnSelectInitialDiplomacyStatement( selection.Key ); end;
                callback = function() 
                    local playerConfig = PlayerConfigurations[ms_SelectedPlayer:GetID()];
                    if (playerConfig ~= nil) then

                        selectedCivName = Locale.Lookup(playerConfig:GetCivilizationShortDescription());
                        m_PopupDialog:Reset();
                        m_PopupDialog:AddText(Locale.Lookup("LOC_DENOUNCE_POPUP_BODY", selectedCivName));
                        m_PopupDialog:AddButton(Locale.Lookup("LOC_CANCEL"), nil);
                        m_PopupDialog:AddButton(Locale.Lookup("LOC_DIPLO_CHOICE_DENOUNCE"), denounceFn, nil, nil, "PopupButtonInstanceRed");
                        m_PopupDialog:Open();

                    end
                end;
            end

            instance.ButtonText:SetText( selectionText );
            if (selection.IsDisabled == nil or selection.IsDisabled == false) then
                instance.Button:RegisterCallback(Mouse.eMouseEnter, function() UI.PlaySound("Main_Menu_Mouse_Over"); end);
                instance.Button:RegisterCallback( Mouse.eLClick, callback );
                instance.ButtonText:SetColor( COLOR_BUTTONTEXT_NORMAL );
                instance.Button:SetDisabled( false );
            else
                instance.ButtonText:SetColor( COLOR_BUTTONTEXT_DISABLED );
                instance.Button:SetDisabled( true );
                if (selection.FailureReasons ~= nil) then
                    instance.Button:SetToolTipString(Locale.Lookup(selection.FailureReasons[1]));
                end
            end
            instance.Button:SetDisabled(not g_bIsLocalPlayerTurn or selection.IsDisabled == true);
        else
            callback = selection.Callback;
            instance.ButtonText:SetColor( COLOR_BUTTONTEXT_NORMAL );
            instance.Button:SetDisabled(not g_bIsLocalPlayerTurn);
            if ( selection.ToolTip ~= nil) then
                tooltipString = Locale.Lookup(selection.ToolTip);
                instance.Button:SetToolTipString(tooltipString);
            else
                instance.Button:SetToolTipString(nil);        -- 清除任何现有的工具提示
            end
        end

        -- 如果字符串被截断，则将完整文本设置为工具提示
        local wasTruncated :boolean = TruncateString(instance.ButtonText, MAX_BEFORE_TRUNC_BUTTON_INST, selectionText);
        if wasTruncated then
            local finalTooltipString    :string    = selectionText;
            if tooltipString ~= nil then
                finalTooltipString = finalTooltipString .. "[NEWLINE]" .. tooltipString;
            end
            instance.Button:SetToolTipString( finalTooltipString );
        end

        -- 在存在的情况下，将工具提示字符串追加到工具提示的末尾
        if selection.Tooltip then
            local currentTooltipString = instance.Button:GetToolTipString();
            instance.Button:SetToolTipString(currentTooltipString .. Locale.Lookup(selection.Tooltip));
        end

        instance.Button:RegisterCallback(Mouse.eMouseEnter, function() UI.PlaySound("Main_Menu_Mouse_Over"); end);
        instance.Button:RegisterCallback( Mouse.eLClick, callback );                    
    end
    if (isSubList) then
        local instance        :table        = buttonIM:GetInstance(stackControl);
        selectionText    = selectionText.. Locale.Lookup("LOC_CANCEL_BUTTON");
        instance.ButtonText:SetText( selectionText );
        instance.Button:SetToolTipString(nil);
        instance.Button:SetDisabled(false);
        instance.ButtonText:SetColor( COLOR_BUTTONTEXT_NORMAL );
        instance.Button:RegisterCallback(Mouse.eMouseEnter, function() UI.PlaySound("Main_Menu_Mouse_Over"); end);
        instance.Button:RegisterCallback( Mouse.eLClick, function() ShowOptionStack(false); end );    
    end
    stackControl:CalculateSize();
end

-- ===========================================================================
-- 此功能允许修改者阻止某些选项出现
-- 在顶级语句列表中。通过覆盖此和函数
-- 在下面，他们可以对初始选项进行细粒度控制。
-- 参数 uiGroup 用户界面组
-- 返回 返回true以添加选项，返回false以阻止添加
function ShouldAddTopLevelStatementOption(uiGroup)
    return true;
end

-- ===========================================================================
-- ===========================================================================
-- 函数：生成并返回基于当前外交状态的初始声明选项集合。
-- 参数：
-- parsedStatements - 包含所有可用解析过的外交声明选项的表格。
-- rootControl - UI 的根控制元素，用于访问和操作UI组件。
-- 返回值：
-- topOptions - 一个包含要在UI中显示的顶级声明选项的表格。

function GetInitialStatementOptions(parsedStatements, rootControl)
    -- 初始化存放不同类型外交选项的表格
    local discussOptions:    table = {};
    local warOptions:        table = {};
    local topOptions:        table = {};

    -- 遍历每个已解析的声明以根据其UIGroup进行分类
    for _, selection in ipairs(parsedStatements) do
        local uiGroup = nil;
        local pActionDef = GameInfo.DiplomaticActions[selection.DiplomaticActionType];
        -- 确保数据存在后再访问
        if pActionDef and pActionDef.UIGroup then 
            uiGroup = pActionDef.UIGroup;
        end
        -- 根据声明的UIGroup分类
        if uiGroup == "DISCUSS" then
            table.insert(discussOptions, selection);
        elseif uiGroup == "FORMALWAR" then
            table.insert(warOptions, selection);
        elseif ShouldAddTopLevelStatementOption(uiGroup) then
            table.insert(topOptions, selection);
        end
    end

    -- 如果讨论选项存在，则添加到顶级选项中
    if(table.count(discussOptions) > 0) then
        table.insert(topOptions, {
            Text            = Locale.Lookup("LOC_DIPLOMACY_DISCUSS").. " [ICON_List]", 
            Callback        = 
                function() 
                    PopulateStatementList( discussOptions, rootControl, true );
                    ShowOptionStack(true);
                end, 
        });
    end

    -- 如果战争选项存在，则添加到顶级选项中
    if(table.count(warOptions) > 0) then
        table.insert(topOptions, {
            Text            = Locale.Lookup("LOC_DIPLOMACY_CASUS_BELLI").. " [ICON_List]", 
            Callback        = 
                function() 
                    PopulateStatementList( warOptions, rootControl, true );
                    ShowOptionStack(true);
                end, 
            ToolTip            = "LOC_DIPLOMACY_CASUS_BELLI_TT"
        });
    end

    -- 返回顶级选项集合
    return topOptions;
end

-- ===========================================================================
-- 函数：填充声明选项，并将它们附加到指定的根控件上。
function AddStatmentOptions(rootControl : table)

    g_ActionListIM:ResetInstances();
    g_SubActionListIM:ResetInstances();

    -- 当本地玩家ID和选中玩家ID均有效时执行以下操作
    if (ms_LocalPlayerID ~= -1 and ms_SelectedPlayerID ~= -1) then
        -- 获取用于填充初始声明的特定声明类型的处理器
        local useStatementType:string = (ms_LocalPlayerID ~= ms_SelectedPlayerID) and "GREETING" or "NO_TARGET";
        local handler = GetStatementHandler(useStatementType);
        
        -- 提取声明选项
        local kParsedStatement = handler.ExtractStatement(handler, useStatementType, "NONE", ms_LocalPlayerID, DiplomacyMoodTypes.ANY, DiplomacyInitiatorTypes.HUMAN);
        handler.RemoveInvalidSelections(kParsedStatement, ms_LocalPlayerID, ms_SelectedPlayerID);

        -- 在此阶段不需要退出选择项
        DiplomacySupport_RemoveSelectionByKey(kParsedStatement, "CHOICE_EXIT");

        -- 如果获取到了有效的声明选项集合
        if kParsedStatement and kParsedStatement.Selections then
            local topOptions:table = GetInitialStatementOptions(kParsedStatement.Selections, rootControl);
            PopulateStatementList(topOptions, rootControl, false);
        end
    end

end

-- ===========================================================================
-- 当激活 intel 关系面板时触发此函数
-- 参数 relationshipInstance: 表示关系实例的表格
function OnActivateIntelRelationshipPanel(relationshipInstance : table)

    local intelSubPanel = relationshipInstance;
    
    -- 获取选中玩家的外交AI
    local selectedPlayerDiplomaticAI = ms_SelectedPlayer:GetDiplomaticAI();
    -- 他们怎么看待我们？
    local iState = selectedPlayerDiplomaticAI:GetDiplomaticStateIndex(ms_LocalPlayerID);
    local kStateEntry = GameInfo.DiplomaticStates[iState];
    local eState = kStateEntry.Hash;
    intelSubPanel.RelationshipText:LocalizeAndSetText( Locale.ToUpper(kStateEntry.Name) );
    -- 填充关系条来反映当前状态
    local relationshipPercent = 1.0;            
    -- 如果处于战争状态，显示特殊的闪烁红色条
    if (eState == DiplomaticStates.WAR) then
        intelSubPanel.FlashingBar:SetHide(false);
        intelSubPanel.AllyBar:SetHide(true);
        intelSubPanel.WarBar:SetHide(false);
        relationshipPercent = .02;
    elseif (eState == DiplomaticStates.ALLIED) then
        intelSubPanel.FlashingBar:SetHide(false);
        intelSubPanel.AllyBar:SetHide(false);
        intelSubPanel.WarBar:SetHide(true);
        relationshipPercent = .92;
    else
        relationshipPercent = kStateEntry.RelationshipLevel / 100;
        intelSubPanel.FlashingBar:SetHide(true);
    end
    intelSubPanel.RelationshipBar:SetPercent(relationshipPercent);
    intelSubPanel.RelationshipIcon:SetOffsetX(relationshipPercent*intelSubPanel.RelationshipBar:GetSizeX());
    intelSubPanel.RelationshipIcon:SetVisState( GetVisStateFromDiplomaticState(iState) );
    
    -- 获取并显示关系原因的工具提示
    local toolTips = selectedPlayerDiplomaticAI:GetDiplomaticModifiers(ms_LocalPlayerID);
    ms_IntelRelationshipReasonIM:ResetInstances();

    if(toolTips) then
        table.sort(toolTips, function(a,b) return a.Score > b.Score; end);

        for i, tip in ipairs(toolTips) do
            local score = tip.Score;
            local text = tip.Text;

            if(score ~= 0) then
                local relationshipReason = ms_IntelRelationshipReasonIM:GetInstance(intelSubPanel.RelationshipReasonStack);
            
                local scoreText = Locale.Lookup("{1_Score : number +#,###.##;-#,###.##}", score);
                if(score > 0) then
                    relationshipReason.Score:SetText("[COLOR_Civ6Green]" .. scoreText .. "[ENDCOLOR]"); -- 正面关系分数显示为绿色
                else
                    relationshipReason.Score:SetText("[COLOR_Civ6Red]" .. scoreText .. "[ENDCOLOR]"); -- 负面关系分数显示为红色
                end

                if(text == "LOC_TOOLTIP_DIPLOMACY_UNKNOWN_REASON") then
                    relationshipReason.Text:SetText("[COLOR_Grey]" .. Locale.Lookup(text) .. "[ENDCOLOR]"); -- 未知原因显示为灰色
                else
                    relationshipReason.Text:SetText(Locale.Lookup(text));
                end
            end
        end
    end

    intelSubPanel.RelationshipReasonStack:CalculateSize();
    if(intelSubPanel.RelationshipReasonStack:GetSizeY()==0) then
        intelSubPanel.NoReasons:SetHide(false); -- 如果没有关系原因，则显示“无原因”文本
    else
        intelSubPanel.NoReasons:SetHide(true);
    end

    if GameCapabilities.HasCapability("CAPABILITY_DIPLOMACY_RELATIONSHIP_INFO") then
        -- 设置顾问图标
        intelSubPanel.AdvisorIcon:SetTexture(IconManager:FindIconAtlas("ADVISOR_GENERIC", 32));

        -- 获取并设置顾问文本
        local advisorText = "";
        local selectedCivName = "";
        -- HACK: 这是暂时伪造的...最终这个列表需要更智能
        local playerConfig = PlayerConfigurations[ms_SelectedPlayer:GetID()];
        if (playerConfig ~= nil) then
            selectedCivName = playerConfig:GetCivilizationDescription();
        end
         
        local advisorTextlower = "[COLOR_Grey]"; -- 顾问提供的可采取行动列表
        advisorTextlower = advisorTextlower .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_OFFER"); 
        advisorTextlower = advisorTextlower .. "[NEWLINE]"; 
--        advisorTextlower = advisorTextlower .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_DENOUNCE", selectedCivName); 
--        advisorTextlower = advisorTextlower .. "[NEWLINE]"; 
        advisorTextlower = advisorTextlower .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_TRADE_ROUTE", selectedCivName); 
        advisorTextlower = advisorTextlower .. "[NEWLINE]"; 
        if (not ms_SelectedPlayer:GetDiplomacy():HasOpenBordersFrom(ms_LocalPlayer:GetID())) then
            advisorTextlower = advisorTextlower .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_OPEN_BORDERS", selectedCivName);
            advisorTextlower = advisorTextlower .. "[NEWLINE]";     
        end
        if (not ms_LocalPlayer:GetDiplomacy():HasDelegationAt(ms_SelectedPlayer:GetID()) and not ms_LocalPlayer:GetDiplomacy():HasEmbassyAt(ms_SelectedPlayer:GetID())) then
            advisorTextlower = advisorTextlower .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_DELEGATION_EMBASSY");
            advisorTextlower = advisorTextlower .. "[NEWLINE]";     
        end
        advisorTextlower = advisorTextlower .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_POSITIVE_AGENDA", selectedCivName); 
        advisorTextlower = advisorTextlower .. "[NEWLINE]";     
        advisorTextlower = advisorTextlower .. "[ENDCOLOR]"; 
        local advisorTextRaise = "[COLOR_Grey]"; -- 顾问提供的可提升关系行动列表
--        advisorTextRaise = advisorTextRaise .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_DENOUNCE_FRIEND", selectedCivName); 
--        advisorTextRaise = advisorTextRaise .. "[NEWLINE]"; 
--        advisorTextRaise = advisorTextRaise .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_DECLARE_FRIENDSHIP", selectedCivName); 
--        advisorTextRaise = advisorTextRaise .. "[NEWLINE]"; 
        advisorTextRaise = advisorTextRaise .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_NEGATIVE_AGENDA", selectedCivName); 
        advisorTextRaise = advisorTextRaise .. "[NEWLINE]"; 
        advisorTextRaise = advisorTextRaise .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_DENOUNCE_THEM"); 
        advisorTextRaise = advisorTextRaise .. "[NEWLINE]"; 
        advisorTextRaise = advisorTextRaise .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_REJECT_PROMISE"); 
        advisorTextRaise = advisorTextRaise .. "[NEWLINE]"; 
        advisorTextRaise = advisorTextRaise .. Locale.Lookup("LOC_DIPLOMACY_ADVISOR_BREAK_PROMISE"); 
        advisorTextRaise = advisorTextRaise .. "[NEWLINE]"; 
        advisorTextRaise = advisorTextRaise .. "[ENDCOLOR]"; 
        intelSubPanel.AdvisorTextRaise:SetText(advisorTextlower);
        intelSubPanel.AdvisorTextLower:SetText(advisorTextRaise);
        intelSubPanel.Advisor:SetHide(false);
    end

end
-- ===========================================================================
-- 函数：激活并更新选定玩家的情报访问级别面板
-- 参数：
-- accessLevelInstance：表示要更新的情报访问级别面板实例的表格。

function OnActivateIntelAccessLevelPanel(accessLevelInstance : table)
    
    local intelSubPanel = accessLevelInstance;
    -- 获取当前选定玩家的外交AI
    local selectedPlayerDiplomaticAI = ms_SelectedPlayer:GetDiplomaticAI();

    -- 获取本地玩家的外交信息
    local localPlayerDiplomacy = ms_LocalPlayer:GetDiplomacy();
    -- 获取本地玩家对选定玩家的可见度等级
    local iAccessLevel = localPlayerDiplomacy:GetVisibilityOn(ms_SelectedPlayerID);

    -- 构建当前访问级别贡献的描述文本
    local accessContributionText = "";
    for row in GameInfo.DiplomaticVisibilitySources () do
        -- 检查选定玩家的可见性来源是否活跃
        if (localPlayerDiplomacy:IsVisibilitySourceActive(ms_SelectedPlayerID, row.Index)) then
            -- 若存在描述，则将其添加到文本中
            if (row.Description ~= nil) then
                if (#accessContributionText > 0) then
                    accessContributionText = accessContributionText .. "[NEWLINE]";
                end
                accessContributionText = accessContributionText .. Locale.Lookup(row.Description);
            end
        end
    end
    
    -- 更新“访问贡献”文本UI元素（若有贡献文本）
    if (#accessContributionText > 0) then
        intelSubPanel.AccessContributionText:SetText(accessContributionText);
        intelSubPanel.AccessContribution:SetHide(false);
    else
        intelSubPanel.AccessContribution:SetHide(true);
    end

    -- 更新访问级别按钮和图标，显示相应的可见度等级信息
    intelSubPanel.AccessLevelText:LocalizeAndSetText(Locale.ToUpper(GameInfo.Visibilities[iAccessLevel].Name));
    -- 调整图标条以显示正确的可见状态
    intelSubPanel.AccessLevelIcon:SetVisState( iAccessLevel-1 );

    -- 根据可见度等级设置共享信息文本
    local szInfoSharedText = "";
    local iNumAdded = 0;
    for row in GameInfo.Gossips () do
        -- 包含在当前访问级别下可见的流言蜚语
        if (row.VisibilityLevel == iAccessLevel) then
            if (row.Description ~= nil) then
                if (iNumAdded > 0) then
                    szInfoSharedText = szInfoSharedText .. "[NEWLINE]";
                end
                szInfoSharedText = szInfoSharedText .. "   " .. Locale.Lookup(row.Description);
                iNumAdded = iNumAdded + 1;
            end
        end
    end

    intelSubPanel.InformationSharedText:SetText(szInfoSharedText);

    -- 如果游戏具备显示下一访问级别信息的能力，则显示之
    if GameCapabilities.HasCapability("CAPABILITY_DIPLOMACY_ACCESS_LEVEL_INFO") then
        -- 显示下一访问级别将获得的信息
        local szNextAccessLevelText = "";
        iNumAdded = 0;
        for row in GameInfo.Gossips () do
            -- 包含在下一访问级别下可见的流言蜚语
            if (row.VisibilityLevel == iAccessLevel + 1) then
                if (row.Description ~= nil) then
                    if (iNumAdded > 0) then
                        szNextAccessLevelText = szNextAccessLevelText .. "[NEWLINE]";
                    end
                    szNextAccessLevelText = szNextAccessLevelText .. "   " .. Locale.Lookup(row.Description);
                    iNumAdded = iNumAdded + 1;
                end
            end
        end
        intelSubPanel.NextAccessLevelText:SetText(szNextAccessLevelText);
        intelSubPanel.NextAccessLevelStack:SetHide(false);

        -- 设置顾问图标
        intelSubPanel.AdvisorIcon:SetTexture(IconManager:FindIconAtlas("ADVISOR_GENERIC", 32));

        -- 设置关于如何提高访问级别的顾问文本
        local advisorText = "";
        for row in GameInfo.DiplomaticVisibilitySources () do
            -- 包括可以采取的激活缺失可见源的行为
            if (not localPlayerDiplomacy:IsVisibilitySourceActive(ms_SelectedPlayerID, row.Index)) then
                if (row.ActionDescription ~= nil) then
                    advisorText = advisorText .. Locale.Lookup(row.ActionDescription).."[NEWLINE]";
                end
            end
        end
        
        -- 更新顾问文本UI元素（若有可采取的行动）
        if (#advisorText > 0) then
            intelSubPanel.AdvisorText:SetText(advisorText);
            intelSubPanel.Advisor:SetHide(false);
        else
            intelSubPanel.Advisor:SetHide(true);
        end
    end
end

-- ===========================================================================
-- 函数：激活并更新选定玩家的情报流言蜚语历史面板
-- 参数：
-- gossipInstance：表示要更新的情报流言蜚语历史面板实例的表格。

function OnActivateIntelGossipHistoryPanel(gossipInstance:table)

    local intelSubPanel = gossipInstance;
    
    -- 获取当前选定玩家的外交AI
    local selectedPlayerDiplomaticAI = ms_SelectedPlayer:GetDiplomaticAI();

    -- 获取本地玩家的外交信息
    local localPlayerDiplomacy = ms_LocalPlayer:GetDiplomacy();

    -- 重置情报流言蜚语历史记录项的实例
    ms_IntelGossipHistoryPanelEntryIM:ResetInstances();

    -- 变量用于跟踪是否已向UI添加任何项目
    local bAddedLastTenTurnsItem = false;
    local bAddedOlderItem = false;

    -- 获取流言蜚语管理器
    local gossipManager = Game.GetGossipManager();

    -- 确定当前回合数
    local iCurrentTurn = Game.GetCurrentGameTurn();

    -- 设置考虑流言蜚语的最早回合数，从前100个回合开始，以避免过多数据
    local earliestTurn = iCurrentTurn - 100;
    -- 获取与选定玩家相关的、本地玩家可见的最近流言蜚语字符串
    local gossipStringTable = gossipManager:GetRecentVisibleGossipStrings(earliestTurn, ms_LocalPlayerID, ms_SelectedPlayerID);
    for i, currTable:table in ipairs(gossipStringTable) do

        -- 获取流言蜚语字符串及其生成时的回合数
        local gossipString = currTable[1];
        local gossipTurn = currTable[2];

        -- 如果存在流言蜚语字符串，则进行处理
        if (gossipString ~= nil) then
            -- 根据其年龄确定将流言蜚语条目添加到哪个堆栈
            local item;
            if ((iCurrentTurn - gossipTurn) <= 10) then
                -- 添加至“最近十回合”堆栈
                item = ms_IntelGossipHistoryPanelEntryIM:GetInstance(intelSubPanel.LastTenTurnsStack);
                bAddedLastTenTurnsItem = true;
                -- 如果流言蜚语在当前或上一回合接收，则标记为新
                if((iCurrentTurn-1) <= gossipTurn) then
                    item.NewIndicator:SetHide(false);
                else
                    item.NewIndicator:SetHide(true);
                end
            else
                -- 添加至“更早”堆栈
                item = ms_IntelGossipHistoryPanelEntryIM:GetInstance(intelSubPanel.OlderStack);
                item.NewIndicator:SetHide(true);
                bAddedOlderItem = true;
            end

            -- 设置流言蜚语文本，并根据需要调整大小
            if (item ~= nil) then
                item.GossipText:SetText(gossipString);            -- 文本已经进行了本地化
                AutoSizeGrid(item:GetTopControl(), item.GossipText,25,37);
            end
        else
            break; -- 如果缺少流言蜚语字符串，则退出循环
        end
    end

    -- 如果未向“最近十回合”堆栈添加任何项目，则显示默认消息
    if (not bAddedLastTenTurnsItem) then
        local item = ms_IntelGossipHistoryPanelEntryIM:GetInstance(intelSubPanel.LastTenTurnsStack);
        item.GossipText:LocalizeAndSetText("LOC_DIPLOMACY_GOSSIP_ITEM_NO_RECENT");
        item.NewIndicator:SetHide(true);
        AutoSizeGrid(item:GetTopControl(), item.GossipText,25,37);
    end

    -- 如果未向“更早”堆栈添加任何项目，则隐藏“更早”标题
    if (not bAddedOlderItem) then
        intelSubPanel.OlderHeader:SetHide(true);
    else
        intelSubPanel.OlderHeader:SetHide(false);
    end
end
-- ===========================================================================
-- 自动调整网格大小以适应标签控制的高度和最小大小限制。
-- 参数 gridControl 表，代表需要调整大小的网格控制对象。
-- 参数 labelControl 表，代表用于计算大小的标签控制对象。
-- 参数 padding 数字，标签高度和网格高度之间的填充值。
-- 参数 minSize 数字，网格控制对象允许的最小高度。
function AutoSizeGrid(gridControl: table, labelControl: table, padding:number, minSize:number)
    local sizeY: number = labelControl:GetSizeY() + padding;
    if (sizeY < minSize) then
        sizeY = minSize;
    end
    gridControl:SetSizeY(sizeY);
end

-- ===========================================================================
-- 获取当前选中的玩家ID。
-- 返回 ms_SelectedPlayerID，当前选中的玩家ID。
function GetSelectedPlayerID()
    return ms_SelectedPlayerID;
end

-- ===========================================================================
-- 在根控制对象中添加一个情报面板。
-- 参数 rootControl 表，代表根控制对象，用于创建情报面板。
function AddIntelPanel(rootControl : table)
    
    -- 重置面板实例
    ms_IntelPanel = nil;
    ms_IntelPanelIM:ResetInstances();

    if (ms_LocalPlayerID ~= -1 and ms_SelectedPlayerID ~= -1 and ms_LocalPlayerID ~= ms_SelectedPlayerID) then
        -- 创建主情报面板
        ms_IntelPanel = ms_IntelPanelIM:GetInstance(rootControl);

        -- 设置标签按钮实例管理器
        ms_IntelTabButtonIM:ResetInstances();
        ms_IntelTabAnchorIM:ResetInstances();

        PopulateIntelPanels(ms_IntelPanel.IntelPanelContainer);

        ShowOverviewPanel();
    end
end

-- ===========================================================================
-- 为情报面板填充内容。
-- 参数 tabContainer 表，代表用于填充内容的情报面板容器。
function PopulateIntelPanels(tabContainer:table)
    AddIntelOverview();
    AddIntelGossip();
    AddIntelAccessLevel();

    -- 如果所选文明是由人类控制的，则不添加此标签页
    local tPlayer :table = Players[ms_SelectedPlayerID];
    if ( tPlayer and not tPlayer:IsHuman() ) then
        AddIntelRelationship();
    end
end

-- ===========================================================================
-- 创建一个标签按钮实例。
-- 返回 返回标签按钮实例。
function CreateTabButton()
    return ms_IntelTabButtonIM:GetInstance(ms_IntelPanel.IntelTabButtonStack);
end

-- ===========================================================================
-- 设置情报面板的标题。
-- 参数 header 字符串，代表要设置的标题文本。
function SetIntelPanelHeader(header:string)
    if ms_IntelPanel == nil then
        return
    end

    ms_IntelPanel.IntelHeader:SetText(header);
end

-- ===========================================================================
-- 添加一个标签到情报面板。
-- 参数 tabPanelIM 表，标签面板的实例管理器。
-- 参数 buttonTooltip 字符串，标签按钮的工具提示文本。
-- 参数 headerText 字符串，标签的标题文本。
-- 参数 buttonIcon 字符串，标签按钮的图标路径。
-- 返回 返回标签面板实例。
function AddIntelTab(tabPanelIM:table, buttonTooltip:string, headerText:string, buttonIcon:string)
    -- 创建标签面板实例
    tabPanelIM:ResetInstances();
    local tabPanelInstance:table = tabPanelIM:GetInstance(ms_IntelPanel.IntelPanelContainer);

    -- 创建标签按钮实例
    local tabButtonInstance:table = ms_IntelTabButtonIM:GetInstance(ms_IntelPanel.IntelTabButtonStack);
    tabButtonInstance.Button:RegisterCallback( Mouse.eLClick, function() ShowPanel(tabPanelInstance:GetTopControl()); end );
    tabButtonInstance.Button:SetToolTipString(buttonTooltip);
    tabButtonInstance.ButtonIcon:SetIcon(buttonIcon);

    -- 缓存按钮实例和标题文本到面板实例
    tabPanelInstance:GetTopControl().m_ButtonInstance = tabButtonInstance;
    tabPanelInstance:GetTopControl().m_HeaderText = headerText;

    -- 返回标签面板实例给调用方
    return tabPanelInstance;
end

-- ===========================================================================
-- 添加一个概览标签到情报面板。
function AddIntelOverview()
    ms_IntelOverviewDividerIM:ResetInstances();
    ms_IntelOverviewAnchorIM:ResetInstances();

    local uiOverviewInst:table = AddIntelTab(ms_IntelOverviewIM, Locale.Lookup("LOC_DIPLOMACY_INTEL_OVERVIEW_COLON_TOOLTIP"), Locale.ToUpper("LOC_DIPLOMACY_INTEL_REPORT_OVERVIEW"), "ICON_OVERVIEW");
    PopulateIntelOverview( uiOverviewInst );
    uiOverviewInst.IntelOverviewStack:CalculateSize();
    LuaEvents.DiploScene_RefreshOverviewRows( GetSelectedPlayerID() );    -- 用于向任何以XP1开始的扩展发送信号。
end

-- ===========================================================================
-- 在概览面板中添加一个分割线。
-- 参数 uiOverviewInst 表，代表概览面板的实例。
function AddIntelOverviewDivider( uiOverviewInst:table )
    ms_IntelOverviewDividerIM:GetInstance( uiOverviewInst.IntelOverviewStack );
end

-- ===========================================================================
-- 为概览面板填充内容。
-- 参数 overviewInstance 表，代表概览面板的实例。
function PopulateIntelOverview( overviewInstance:table )
    -- 按显示顺序添加概览行
    AddOverviewGossip(overviewInstance);
    AddIntelOverviewDivider(overviewInstance);
    AddOverviewAccessLevel(overviewInstance);
    AddIntelOverviewDivider(overviewInstance);
    AddOverviewGovernment(overviewInstance);
    AddIntelOverviewDivider(overviewInstance);
    if (AddOverviewAgendas(overviewInstance)) then
        -- 只有在可见时才添加分割线
        AddIntelOverviewDivider(overviewInstance);
    end
    if (AddOverviewAgreements(overviewInstance)) then
        -- 只有在可见时才添加分割线
        AddIntelOverviewDivider(overviewInstance);
    end
    if (AddOverviewOurRelationship(overviewInstance)) then
        -- 只有在可见时才添加分割线
        AddIntelOverviewDivider(overviewInstance);
    end
    AddOverviewOtherRelationships(overviewInstance);
end

-- ===========================================================================
-- 获取概览锚点。
-- 参数 parent 表，代表父级控制对象。
-- 返回 返回概览锚点的实例。
function GetOverviewAnchor(parent:table)
    return ms_IntelOverviewAnchorIM:GetInstance(parent);
end

-- ===========================================================================
-- 获取标签锚点。
-- 参数 parent 表，代表父级控制对象。
-- 返回 返回标签锚点的实例。
function GetTabAnchor(parent:table)
    return ms_IntelTabAnchorIM:GetInstance(parent);
end

-- ===========================================================================
-- 为指定的概述实例添加综述闲话
-- 参数 overviewInstance 表示概述实例的表格
function AddOverviewGossip(overviewInstance:table)
    ms_IntelOverviewGossipIM:ResetInstances();
    local overviewGossipInst:table = ms_IntelOverviewGossipIM:GetInstance(overviewInstance.IntelOverviewStack);

    -- 确定过去两个回合是否存在闲话
    local gossipThisTurn:number = 0;
    local gossipStringTable = Game.GetGossipManager():GetRecentVisibleGossipStrings(Game.GetCurrentGameTurn()-1, ms_LocalPlayerID, ms_SelectedPlayerID);
    for i,gossip in pairs(gossipStringTable) do
        gossipThisTurn = gossipThisTurn + 1;
    end

    if (gossipThisTurn > 0) then
        overviewGossipInst.GossipText:SetText(Locale.Lookup("LOC_DIPLOMACY_GOSSIP_ITEM_COUNT", gossipThisTurn));
    else
        overviewGossipInst.GossipText:SetText(Locale.Lookup("LOC_DIPLOMACY_GOSSIP_ITEM_NONE_THIS_TURN"));
    end
end

-- ===========================================================================

-- 为指定的概述实例添加概述访问级别
-- 参数 overviewInstance 表示概述实例的表格
function AddOverviewAccessLevel(overviewInstance:table)
    ms_IntelOverviewAccessLevelIM:ResetInstances();
    local overviewAccessLevelInst:table = ms_IntelOverviewAccessLevelIM:GetInstance(overviewInstance.IntelOverviewStack);

    local localPlayerDiplomacy = ms_LocalPlayer:GetDiplomacy();
    local iAccessLevel = localPlayerDiplomacy:GetVisibilityOn(ms_SelectedPlayerID);

    -- 根据可见性状态，将访问级别图标移至正确位置
    if( iAccessLevel == 0) then
        overviewAccessLevelInst.AccessLevelIcon:SetHide(true);
    else
        overviewAccessLevelInst.AccessLevelIcon:SetHide(false);
        overviewAccessLevelInst.AccessLevelIcon:SetVisState( iAccessLevel-1 );
    end

    overviewAccessLevelInst.AccessLevelText:LocalizeAndSetText(GameInfo.Visibilities[iAccessLevel].Name);
end

-- ===========================================================================

-- 为指定的概述实例添加政府概述
-- 参数 overviewInstance 表示概述实例的表格
function AddOverviewGovernment(overviewInstance:table)
    ms_IntelOverviewGovernmentIM:ResetInstances();
    local overviewGovernmentInst:table = ms_IntelOverviewGovernmentIM:GetInstance(overviewInstance.IntelOverviewStack);

    -- 确定被选中玩家的政府类型
    local eSelectedPlayerGovernment:number = ms_SelectedPlayer:GetCulture():GetCurrentGovernment();
    if eSelectedPlayerGovernment ~= -1 then
        overviewGovernmentInst.GovernmentText:LocalizeAndSetText( GameInfo.Governments[eSelectedPlayerGovernment].Name );
    elseif ms_SelectedPlayer:GetCulture():IsInAnarchy() then
        local iAnarchyTurns = ms_SelectedPlayer:GetCulture():GetAnarchyEndTurn() - Game.GetCurrentGameTurn();
        overviewGovernmentInst.GovernmentText:LocalizeAndSetText( "LOC_GOVERNMENT_ANARCHY_TURNS", iAnarchyTurns );
    else
        overviewGovernmentInst.GovernmentText:LocalizeAndSetText( "LOC_DIPLOMACY_GOVERNMENT_NONE" );
    end
end

-- ===========================================================================
-- 获取情报概述中的议程信息
-- 返回 返回 ms_IntelOverviewAgendasIM 实例
function GetIntelOverviewAgendas()
    return ms_IntelOverviewAgendasIM;
end

-- ===========================================================================
-- 获取情报概述中的议程条目信息
-- 返回 返回 ms_IntelOverviewAgendaEntryIM 实例
function GetIntelOverviewAgendaEntries()
    return ms_IntelOverviewAgendaEntryIM;
end

-- ===========================================================================
-- 为指定的概述实例添加议程概述
-- 参数 overviewInstance 表示概述实例的表格
function AddOverviewAgendas(overviewInstance:table)
    ms_IntelOverviewAgendasIM:ResetInstances();
    local overviewAgendasInst:table = ms_IntelOverviewAgendasIM:GetInstance(overviewInstance.IntelOverviewStack);

    ms_IntelOverviewAgendaEntryIM:ResetInstances();

    if (PlayerConfigurations[ms_SelectedPlayerID]:IsHuman()) then
        -- 人类玩家没有可以显示的议程
        overviewAgendasInst.Top:SetHide(true);
    else
        overviewAgendasInst.Top:SetHide(false);
        -- 确定被选中玩家的领导者类型
        local leader:string = PlayerConfigurations[ms_SelectedPlayerID]:GetLeaderTypeName();
        local hasHistoricalAgenda = false;

        for row in GameInfo.HistoricalAgendas() do
            if(row.LeaderType == leader) then
                local agendaType = row.AgendaType;
                local agenda = GameInfo.Agendas[agendaType];
                if(agenda) then
                    local historicalAgenda = ms_IntelOverviewAgendaEntryIM:GetInstance(overviewAgendasInst.OverviewAgendasStack);
                    historicalAgenda.Text:LocalizeAndSetText( GameInfo.Agendas[agendaType].Name );
                    historicalAgenda.Text:LocalizeAndSetToolTip( GameInfo.Agendas[agendaType].Description );
                    hasHistoricalAgenda = true;
                    break;
                end
            end
        end

        local localPlayerDiplomacy = ms_LocalPlayer:GetDiplomacy();
        local iAccessLevel = localPlayerDiplomacy:GetVisibilityOn(ms_SelectedPlayerID);

        -- 确定我们是否达到了可以揭示随机议程的外交可见性水平
        local bRevealRandom = false;
        for row in GameInfo.Visibilities() do
            if (row.Index <= iAccessLevel and row.RevealAgendas == true) then
                bRevealRandom = true;
            end
        end
        local kAgendaTypes = {};
        kAgendaTypes = ms_SelectedPlayer:GetAgendaTypes();
        -- GetAgendaTypes() 返回包括历史议程在内的所有议程类型。
        -- 要仅检索随机分配的议程，从表中删除第一个条目。
        table.remove(kAgendaTypes,1);
        local numRandomAgendas = table.count(kAgendaTypes);
        if (numRandomAgendas > 0) then
            if(bRevealRandom) then
                -- 如果我们的可见性允许，显示议程
                -- 目前，如果我们达到了秘密级别，我们将显示所有随机议程。
                local bFirst = true;
                for i, agendaType in ipairs(kAgendaTypes) do 
                    local randomAgenda = ms_IntelOverviewAgendaEntryIM:GetInstance(overviewAgendasInst.OverviewAgendasStack);
                    randomAgenda.Text:LocalizeAndSetText( GameInfo.Agendas[agendaType].Name );
                    randomAgenda.Text:LocalizeAndSetToolTip( GameInfo.Agendas[agendaType].Description );
                end
            else
                -- 否则，显示有多少隐藏议程，并激励玩家提高可见性以查看它们！
                local hiddenAgenda = ms_IntelOverviewAgendaEntryIM:GetInstance(overviewAgendasInst.OverviewAgendasStack);
                hiddenAgenda.Text:LocalizeAndSetText("LOC_DIPLOMACY_HIDDEN_AGENDAS",numRandomAgendas, numRandomAgendas>1);
                hiddenAgenda.Text:LocalizeAndSetToolTip("LOC_DIPLOMACY_HIDDEN_AGENDAS_TT");
            end
        elseif (numRandomAgendas == 0) then
            local noRandomAgendas = ms_IntelOverviewAgendaEntryIM:GetInstance(overviewAgendasInst.OverviewAgendasStack);
            noRandomAgendas.Text:LocalizeAndSetText("LOC_DIPLOMACY_RANDOM_AGENDA_NONE");
            noRandomAgendas.Text:SetToolTipString(nil); -- 为此情况禁用工具提示
        end
    end

    return not overviewAgendasInst.Top:IsHidden();
end

-- ===========================================================================
-- 为指定的概述实例添加协议概述
-- 参数 overviewInstance 表示概述实例的表格
function AddOverviewAgreements(overviewInstance:table)
    ms_IntelOverviewAgreementsIM:ResetInstances();
    local overviewAgreementsInst:table = ms_IntelOverviewAgreementsIM:GetInstance(overviewInstance.IntelOverviewStack);

    local localPlayerDiplomacy = ms_LocalPlayer:GetDiplomacy();
    ms_IconOnlyIM:ResetInstances();

    if (localPlayerDiplomacy:HasDelegationAt(ms_SelectedPlayer:GetID())) then
        AddAgreementEntry(overviewAgreementsInst, "ICON_DIPLOACTION_DIPLOMATIC_DELEGATION", "LOC_DIPLO_MODIFIER_DELEGATION");
    end
    if(localPlayerDiplomacy:HasEmbassyAt(ms_SelectedPlayer:GetID())) then
        AddAgreementEntry(overviewAgreementsInst, "ICON_DIPLOACTION_RESIDENT_EMBASSY", "LOC_DIPLO_MODIFIER_RESIDENT_EMBASSY");
    end
    if(localPlayerDiplomacy:HasDefensivePact(ms_SelectedPlayer:GetID())) then
        AddAgreementEntry(overviewAgreementsInst, "ICON_DIPLOACTION_DEFENSIVE_PACT", "LOC_DIPLO_MODIFIER_DEFENSIVE_PACT");
    end
    if(localPlayerDiplomacy:HasOpenBordersFrom(ms_SelectedPlayer:GetID())) then
        AddAgreementEntry(overviewAgreementsInst, "ICON_DIPLOACTION_OPEN_BORDERS", "LOC_DIPLO_MODIFIER_RECEIVED_OPEN_BORDERS");
    end
    if(ms_SelectedPlayer:GetDiplomacy():HasOpenBordersFrom(ms_LocalPlayer:GetID())) then
        AddAgreementEntry(overviewAgreementsInst, "ICON_DIPLOACTION_GIVE_OPEN_BORDERS", "LOC_DIPLO_MODIFIER_GAVE_OPEN_BORDERS");
    end
    if(localPlayerDiplomacy:GetResearchAgreementTech(ms_SelectedPlayer:GetID()) ~= -1) then
        AddAgreementEntry(overviewAgreementsInst, "ICON_DIPLOACTION_RESEARCH_AGREEMENT", "LOC_DIPLOACTION_RESEARCH_AGREEMENT_NAME");
    end
    if(localPlayerDiplomacy:IsFightingAnyJointWarWith(ms_SelectedPlayer:GetID())) then
        AddAgreementEntry(overviewAgreementsInst, "ICON_DIPLOACTION_JOINT_WAR", "LOC_DIPLOACTION_JOINT_WAR_NAME");
    end

    -- 如果没有显示的协议条目，则隐藏顶部部分
    if(ms_IconOnlyIM.m_iAllocatedInstances <= 0) then
        overviewAgreementsInst.Top:SetHide(true);
    else
        overviewAgreementsInst.Top:SetHide(false);
    end

    return not overviewAgreementsInst.Top:IsHidden();
end

-- ===========================================================================
-- 向协议实例添加协议条目
-- 参数 agreementInstance 协议实例
-- 参数 icon 协议图标标识
-- 参数 tooltip 协议工具提示文本
function AddAgreementEntry(agreementInstance, icon, tooltip)
    -- 向协议实例添加具有指定图标的协议条目，并设置工具提示文本
    local agreement = ms_IconOnlyIM:GetInstance(agreementInstance.AgreementStack);
    agreement.Icon:SetIcon(icon);
    agreement.Icon:SetToolTipString(Locale.Lookup(tooltip));
end

-- ===========================================================================
-- 添加概述我们之间的关系
-- 参数 overviewInstance:table 概览实例，包含IntelOverviewStack等信息
-- 返回 boolean 如果顶部按钮不应隐藏，则返回true；否则返回false
function AddOverviewOurRelationship(overviewInstance:table)
    ms_IntelOverviewOurRelationshipIM:ResetInstances(); -- 重置所有关系实例
    local overviewOurRelationshipInst:table = ms_IntelOverviewOurRelationshipIM:GetInstance(overviewInstance.IntelOverviewStack); -- 获取我们和选定玩家的关系实例

    print ("In AddOverviewOurRelationship");

    -- 关系面板按钮
    if (PlayerConfigurations[ms_SelectedPlayerID]:IsHuman()) then
        -- 对于人类玩家，不显示任何计算得出的关系
        overviewOurRelationshipInst.Top:SetHide(true);
    else
        -- 获取选定玩家的外交AI
        local selectedPlayerDiplomaticAI = ms_SelectedPlayer:GetDiplomaticAI();
        -- 他们怎么看待我们？
        local iState :number = selectedPlayerDiplomaticAI:GetDiplomaticStateIndex(ms_LocalPlayerID);
        local relationshipString:string = Locale.Lookup(GameInfo.DiplomaticStates[iState].Name); -- 查找关系字符串
        -- 如果是我们自己的团队，将团队名称添加到关系文本中
        if Players[ms_LocalPlayerID]:GetTeam() == Players[ms_SelectedPlayerID]:GetTeam() then
            relationshipString = "(" .. Locale.Lookup("LOC_WORLD_RANKINGS_TEAM", Players[ms_LocalPlayerID]:GetTeam()) .. ") " .. relationshipString;
        end
        overviewOurRelationshipInst.RelationshipText:SetText( relationshipString );

        local localPlayerDiplomacy = ms_LocalPlayer:GetDiplomacy();

        -- 设置谴责状态的工具提示
        if (GameInfo.DiplomaticStates[iState].StateType == "DIPLO_STATE_DENOUNCED") then
            local szDenounceTooltip;
            local iRemainingTurns;
            local iOurDenounceTurn = localPlayerDiplomacy:GetDenounceTurn(ms_SelectedPlayerID);
            local iTheirDenounceTurn = Players[ms_SelectedPlayerID]:GetDiplomacy():GetDenounceTurn(ms_LocalPlayerID);
            local iPlayerOrderAdjustment = 0;
            -- 计算谴责期限和调整玩家顺序
            if (iTheirDenounceTurn >= iOurDenounceTurn) then
                if (ms_SelectedPlayerID > ms_LocalPlayerID) then
                    iPlayerOrderAdjustment = 1;
                end
            else
                if (ms_LocalPlayerID > ms_SelectedPlayerID) then
                    iPlayerOrderAdjustment = 1;
                end
            end
            print ("Adjustment: " .. tostring(iPlayerOrderAdjustment));
            if (iOurDenounceTurn >= iTheirDenounceTurn) then  
                iRemainingTurns = 1 + iOurDenounceTurn + Game.GetGameDiplomacy():GetDenounceTimeLimit() - Game.GetCurrentGameTurn() + iPlayerOrderAdjustment;
                szDenounceTooltip = Locale.Lookup("LOC_DIPLOMACY_DENOUNCED_TOOLTIP", PlayerConfigurations[ms_LocalPlayerID]:GetCivilizationShortDescription(), PlayerConfigurations[ms_SelectedPlayerID]:GetCivilizationShortDescription());
            else
                iRemainingTurns = 1 + iTheirDenounceTurn + Game.GetGameDiplomacy():GetDenounceTimeLimit() - Game.GetCurrentGameTurn() + iPlayerOrderAdjustment;
                szDenounceTooltip = Locale.Lookup("LOC_DIPLOMACY_DENOUNCED_TOOLTIP", PlayerConfigurations[ms_SelectedPlayerID]:GetCivilizationShortDescription(), PlayerConfigurations[ms_LocalPlayerID]:GetCivilizationShortDescription());
            end
            szDenounceTooltip = szDenounceTooltip .. " [" .. Locale.Lookup("LOC_ESPIONAGEPOPUP_TURNS_REMAINING", iRemainingTurns) .. "]";
            overviewOurRelationshipInst.RelationshipText:SetToolTipString(szDenounceTooltip);
        -- 设置声明为朋友状态的工具提示
        elseif (GameInfo.DiplomaticStates[iState].StateType == "DIPLO_STATE_DECLARED_FRIEND") then
            local szFriendTooltip;
            local iFriendshipTurn = localPlayerDiplomacy:GetDeclaredFriendshipTurn(ms_SelectedPlayerID);
            local iRemainingTurns = iFriendshipTurn + Game.GetGameDiplomacy():GetDenounceTimeLimit() - Game.GetCurrentGameTurn();
            szFriendTooltip = Locale.Lookup("LOC_DIPLOMACY_DECLARED_FRIENDSHIP_TOOLTIP", PlayerConfigurations[ms_LocalPlayerID]:GetCivilizationShortDescription(), PlayerConfigurations[ms_SelectedPlayerID]:GetCivilizationShortDescription(), iRemainingTurns);
            overviewOurRelationshipInst.RelationshipText:SetToolTipString(szFriendTooltip);
        else
            overviewOurRelationshipInst.RelationshipText:SetToolTipString(nil);
        end
        -- 根据外交状态设置关系图标可见状态
        overviewOurRelationshipInst.RelationshipIcon:SetVisState(GetVisStateFromDiplomaticState(iState));
    end

    return not overviewOurRelationshipInst.Top:IsHidden(); -- 返回顶部按钮是否隐藏的相反状态
end

-- ===========================================================================
-- 为概述界面添加其他关系信息
-- 参数 overviewInstance:table 概述实例对象，包含IntelOverviewStack等信息
-- 返回 boolean 如果关系堆栈不隐藏，则返回true；否则返回false
function AddOverviewOtherRelationships(overviewInstance:table)
    -- 重置其他关系实例
    ms_IntelOverviewOtherRelationshipsIM:ResetInstances();
    -- 获取当前概述实例的其他关系堆栈实例
    local overviewOtherRelationshipsInst:table = ms_IntelOverviewOtherRelationshipsIM:GetInstance(overviewInstance.IntelOverviewStack);

    -- 初始化关系区域数据
    local pLocalPlayerDiplomacy:table = ms_LocalPlayer:GetDiplomacy();
    
    -- 重置关系图标实例
    ms_RelationshipIconsIM:ResetInstances();

    -- 遍历所有已遇见的玩家，设置关系图标
    local selectedPlayerDiplomacy = ms_SelectedPlayer:GetDiplomacy();
    local aPlayers = PlayerManager.GetAliveMajors();
    for _, pPlayer in ipairs(aPlayers) do
        local playerID :number = pPlayer:GetID();
        -- 筛选主要玩家，且不是本地玩家和选中玩家，且已经遇见
        if (pPlayer:IsMajor() and playerID ~= ms_LocalPlayerID and playerID ~= ms_SelectedPlayer:GetID() and selectedPlayerDiplomacy:HasMet(playerID)) then
            local playerConfig        :table = PlayerConfigurations[playerID];
            local leaderTypeName    :string = playerConfig:GetLeaderTypeName();
            if (leaderTypeName ~= nil) then
                -- 获取关系图标实例
                local relationshipIcon    :table = ms_RelationshipIconsIM:GetInstance(overviewOtherRelationshipsInst.RelationshipsStack);
                -- 获取玩家间的外交状态
                local iPlayerDiploState    :number= pPlayer:GetDiplomaticAI():GetDiplomaticStateIndex(ms_SelectedPlayer:GetID());                
                local kRelationship        :table = GameInfo.DiplomaticStates[iPlayerDiploState];
                local isRelationHidden    :boolean = true;

                -- 如果关系不是中立状态，根据玩家类型设置关系规则和可见性
                if (kRelationship.Hash ~= DiplomaticStates.NEUTRAL) then
                    local isHuman        :boolean= not (ms_SelectedPlayer:IsAI() or pPlayer:IsAI());
                    local relationType    :string = kRelationship.StateType;
                    local isValid        :boolean= (isHuman and Relationship.IsValidWithHuman( relationType )) or (not isHuman and Relationship.IsValidWithAI( relationType ));
                    if isValid then
                        relationshipIcon.Status:SetToolTipString( Locale.Lookup(kRelationship.Name) );
                        relationshipIcon.Status:SetVisState( iPlayerDiploState );
                        isRelationHidden = false;
                    end
                end
                relationshipIcon.Status:SetHide( isRelationHidden );

                -- 如果本地玩家已经遇见该玩家，设置玩家图标和工具提示
                if( pLocalPlayerDiplomacy:HasMet(playerID) ) then
                    relationshipIcon.Icon:SetTexture(IconManager:FindIconAtlas("ICON_" .. playerConfig:GetLeaderTypeName(), 32));
                    -- 设置团队锦带（如果该玩家属于一个多人团队）
                    local leaderDesc :string = playerConfig:GetLeaderName();
                    relationshipIcon.Background:LocalizeAndSetToolTip("LOC_DIPLOMACY_DEAL_PLAYER_PANEL_TITLE", leaderDesc, playerConfig:GetCivilizationDescription());
                    
                    -- 显示团队锦带（如果团队成员超过一人）
                    local teamID:number = playerConfig:GetTeam();
                    if #Teams[teamID] > 1 then
                        local teamRibbonName:string = TEAM_RIBBON_PREFIX .. tostring(teamID);
                        relationshipIcon.TeamRibbon:SetIcon(teamRibbonName, TEAM_RIBBON_SMALL_SIZE);
                        relationshipIcon.TeamRibbon:SetHide(false);
                        relationshipIcon.TeamRibbon:SetColor(GetTeamColor(teamID));
                    else
                        -- 如果团队只包含一个玩家，隐藏团队锦带
                        relationshipIcon.TeamRibbon:SetHide(true);
                    end
                else
                    -- 如果本地玩家未遇见该玩家，设置为默认领袖图标和“未遇见的文明”工具提示
                    relationshipIcon.Icon:SetTexture(IconManager:FindIconAtlas("ICON_LEADER_DEFAULT", 32));
                    relationshipIcon.Background:LocalizeAndSetToolTip("LOC_DIPLOPANEL_UNMET_PLAYER");
                    relationshipIcon.TeamRibbon:SetHide(true);
                end
            end                
        end
    end

    -- 计算关系堆栈的大小
    overviewOtherRelationshipsInst.RelationshipsStack:CalculateSize();

    -- 如果该文明与其他任何文明都没有关系，隐藏关系堆栈
    if ( overviewOtherRelationshipsInst.RelationshipsStack:GetSizeY() == 0) then
        overviewOtherRelationshipsInst.Top:SetHide(true);
    else
        overviewOtherRelationshipsInst.Top:SetHide(false);
    end

    -- 返回关系堆栈是否被隐藏
    return not overviewOtherRelationshipsInst.Top:IsHidden();
end

-- ===========================================================================
-- 添加情报闲聊选项
-- 创建一个新的情报标签页，用于显示闲聊情报。不接受参数，也不返回值。
function AddIntelGossip()
    local gossipInstance:table = AddIntelTab(ms_IntelGossipIM, Locale.Lookup("LOC_DIPLOMACY_INTEL_GOSSIP_COLON_TOOLTIP"), Locale.ToUpper("LOC_DIPLOMACY_INTEL_REPORT_GOSSIP"), "ICON_GOSSIP");
    OnActivateIntelGossipHistoryPanel(gossipInstance);
end

-- ===========================================================================
-- 添加情报访问等级选项
-- 创建一个新的情报标签页，用于显示访问等级情报。不接受参数，也不返回值。
function AddIntelAccessLevel()
    local accessLevelInstance:table = AddIntelTab(ms_IntelAccessLevelIM, Locale.Lookup("LOC_DIPLOMACY_INTEL_ACCESS_LEVEL_COLON_TOOLTIP"), Locale.ToUpper("LOC_DIPLOMACY_INTEL_REPORT_ACCESS_LEVEL"), "ICON_ACCESS_LEVEL");
    OnActivateIntelAccessLevelPanel(accessLevelInstance);
end

-- ===========================================================================
-- 添加情报关系选项
-- 创建一个新的情报标签页，用于显示国家间关系情报。不接受参数，也不返回值。
function AddIntelRelationship()
    local relationshipInstance:table = AddIntelTab(ms_IntelRelationshipIM, Locale.Lookup("LOC_DIPLOMACY_INTEL_OUR_RELATIONSHIP_TOOLTIP"), Locale.ToUpper("LOC_DIPLOMACY_INTEL_REPORT_RELATIONSHIP"), "ICON_RELATIONSHIP");
    OnActivateIntelRelationshipPanel(relationshipInstance);
end

-- ===========================================================================
-- 显示概览面板
-- 无参数
-- 无返回值
function ShowOverviewPanel()
    local overviewInstance:table = ms_IntelOverviewIM:GetAllocatedInstance();
    if overviewInstance ~= nil then
        ShowPanel(overviewInstance:GetTopControl());
    end
end

-- ===========================================================================
-- 显示指定的面板
-- 参数 panelInstance:table 需要显示的面板实例
-- 无返回值
function ShowPanel(panelInstance:table)
    if ms_IntelPanel == nil then
        return;
    end

    -- 显示指定的面板
    if panelInstance then
        panelInstance:SetHide(false);
    end

    -- 隐藏其他面板
    local intelPanelList = ms_IntelPanel.IntelPanelContainer:GetChildren();
    for i, child in ipairs(intelPanelList) do
        if child ~= panelInstance then
            child:SetHide(true);
        end
    end

    -- 选中传入的标签按钮，并取消其他按钮的选中状态
    if panelInstance.m_ButtonInstance ~= nil then
        for i=1, ms_IntelTabButtonIM.m_iCount, 1 do
            local buttonInstance:table = ms_IntelTabButtonIM:GetAllocatedInstance(i);
            if buttonInstance then
                if buttonInstance == panelInstance.m_ButtonInstance then
                    buttonInstance.Button:SetSelected(true);
                else
                    buttonInstance.Button:SetSelected(false);
                end
            end
        end
    end

    -- 更新情报面板头部信息
    if panelInstance.m_HeaderText then
        ms_IntelPanel.IntelHeader:SetText(panelInstance.m_HeaderText);
    end

    -- 重新计算滚动面板的大小以适应容器
    ms_IntelPanel.IntelPanelContainer:CalculateSize();
end

-- ===========================================================================
-- 为玩家面板填充内容
-- 参数 rootControl:table 根控制元素
-- 参数 player:table 玩家信息
-- 无返回值
function PopulatePlayerPanel(rootControl : table, player : table)
    if (player ~= nil) then
        -- 添加语句以使用该堆栈的大小来确定情报容器的大小
        
        AddStatmentOptions(rootControl);
        

        AddIntelPanel(rootControl.IntelContainer);
        
        -- 监听选项堆栈大小变化以调整情报面板大小
        rootControl.RootOptionStack:RegisterSizeChanged( OnRootOptionStackSizeChanged );
    end
end

-- ===========================================================================
-- 根据是否显示子选项来展示选项堆栈
-- 参数 showSubOptions:boolean 是否显示子选项
-- 无返回值
function ShowOptionStack(showSubOptions:boolean)
    if not m_LiteMode then
        ms_PlayerPanel.OptionStack:SetHide(showSubOptions);
        ms_PlayerPanel.SubOptionStack:SetHide(not showSubOptions);
    else
        ms_PlayerPanel.RootOptionStack:SetHide(true);
        ms_PlayerPanel.IntelContainer:SetSizeY(ms_PlayerPanel.ContentContainer:GetSizeY());
    end
end

-- ===========================================================================
-- 当根选项堆栈的大小改变时调用，用于调整界面元素的尺寸。
function OnRootOptionStackSizeChanged()
    if ms_PlayerPanel ~= nil then
        -- 重新计算IntelContainer的大小以填充堆栈
        ms_PlayerPanel.RootOptionStack:CalculateSize();
        local fillSize:number = ms_PlayerPanel.ContentContainer:GetSizeY() - ms_PlayerPanel.RootOptionStack:GetSizeY();
        ms_PlayerPanel.IntelContainer:SetSizeY(fillSize);
    end
end

-- ===========================================================================
-- 为玩家面板头部填充信息，包括文明图标和领袖名称。
-- 参数 rootControl 面板的根控制元素
-- 参数 player 当前玩家对象
function PopulatePlayerPanelHeader(rootControl : table, player : table)
    if (player ~= nil) then
        local playerConfig = PlayerConfigurations[player:GetID()];
        if (playerConfig ~= nil) then
            -- 设置文明图标
            local civIconController = CivilizationIcon:AttachInstance(rootControl.CivIcon);
            civIconController:UpdateIconFromPlayerID(player:GetID());

            -- 设置领袖名称
            local leaderDesc = playerConfig:GetLeaderName();
            rootControl.PlayerNameText:LocalizeAndSetText( Locale.ToUpper( Locale.Lookup(leaderDesc)));
            rootControl.CivNameText:LocalizeAndSetText( Locale.ToUpper( Locale.Lookup(playerConfig:GetCivilizationDescription())));
        end
    end
end


-- ===========================================================================
-- 为指定玩家配置领袖图标。
-- 参数 leaderIcon 领袖图标的控制元素
-- 参数 player 当前玩家对象
-- 参数 isUniqueLeader 是否为独特的领袖标志
function PopulateLeader(leaderIcon : table, player : table, isUniqueLeader : boolean)
    if (player ~= nil and player:IsMajor()) then
        local playerID = player:GetID();
        local playerConfig = PlayerConfigurations[playerID];
        if (playerConfig ~= nil) then
            local leaderTypeName = playerConfig:GetLeaderTypeName();
            if (leaderTypeName ~= nil) then
                -- 设置图标
                local iconName = "ICON_" .. leaderTypeName;
                leaderIcon:UpdateIcon(iconName, playerID, isUniqueLeader);
                
                -- 配置按钮事件
                leaderIcon.Controls.SelectButton:SetVoid1(playerID);
                leaderIcon:RegisterCallback(Mouse.eMouseEnter, function() UI.PlaySound("Main_Menu_Mouse_Over"); end);
                leaderIcon:RegisterCallback(Mouse.eLClick, OnPlayerSelected);

                -- 设置选择背景的隐藏状态
                leaderIcon.Controls.SelectedBackground:SetHide(playerID ~= ms_SelectedPlayerID);
            end
        end
    end
end

-- ===========================================================================
-- 切换到对话模式，隐藏其他界面元素。
-- 参数 player 当前对话的玩家对象
function SetConversationMode(player : table)
    -- 显示对话容器，隐藏概览容器
    Controls.ConversationContainer:SetHide(false);
    Controls.LeaderResponse_Alpha:Play();
    Controls.ConversationSelection_Alpha:Play();
    Controls.LeaderResponse_Slide:Play();
    Controls.ConversationSelection_Slide:Play();
    
    Controls.OverviewContainer:SetHide(true);

    ms_currentViewMode = CONVERSATION_MODE;
end

-- ===========================================================================
--    Centers scroll panel (if possible) on a specfic type.
--  在特定类型上居中滚动面板（如果可能）。
-- ===========================================================================
-- ScrollToNode: 根据指定玩家ID，滚动到玩家在外交横条上的相应位置
-- 参数:
-- playerID: 玩家ID，类型为数字
function ScrollToNode( playerID:number )
    local percent:number = 0;
    local scrollHeight = ms_DiplomacyRibbon.LeaderRibbonScroll:GetSizeY();
    -- 如果玩家坐标已知，则计算滚动百分比
    if (m_LeaderCoordinates ~= nil) and (m_LeaderCoordinates[playerID] ~= nil) then
        local y        = m_LeaderCoordinates[playerID] - ( scrollHeight * 0.4);
        local size  = (scrollHeight / ms_DiplomacyRibbon.LeaderRibbonScroll:GetRatio()) - scrollHeight;
        percent = math.clamp( y  / size, 0, 1);
    end
    ms_DiplomacyRibbon.LeaderRibbonScroll:SetScrollValue(percent);   
end

-- ===========================================================================
-- SelectPlayer: 选择一个玩家，并根据指定模式更新UI显示
-- 参数:
-- playerID: 要选择的玩家ID
-- mode: 查看模式（对话模式、电影模式、概览模式）
-- refresh: 是否刷新当前视图
-- allowDeadPlayer: 是否允许选择已淘汰的玩家
function SelectPlayer(playerID, mode, refresh, allowDeadPlayer)
    -- 处理模式和刷新标志的默认值
    print("SelectPlayer: ", playerID)
    if (mode == nil) then
        mode = ms_currentViewMode;
    end
    if (refresh == nil) then
        refresh = false;
    end

    if (allowDeadPlayer == nil) then
        allowDeadPlayer = false;
    end

    local isDifferentPlayer = false;
    -- 检查是否选择了不同的玩家或模式，或者需要强制刷新
    if (ms_SelectedPlayerID ~= playerID or mode ~= ms_currentViewMode or refresh == true) then
        isDifferentPlayer = true;

        -- 在横条上取消当前玩家的选择
        if (ms_SelectedPlayerID ~= -1) then
            local ribbonEntry = ms_LeaderIDToRibbonEntry[ms_SelectedPlayerID];
            if (ribbonEntry ~= nil) then
                ribbonEntry.SelectedBackground:SetHide( true );
            end
        end

        -- 更新选中玩家，检查是否是第一次打开
        if (m_firstOpened == false) then
            if (ms_SelectedPlayerID == playerID) then
                isDifferentPlayer = false;
            end
        end
        ms_SelectedPlayerID = playerID;
        UpdateSelectedPlayer(allowDeadPlayer);       -- 确保选中的玩家是有效的

        -- 如果选中的玩家非-1，那么更新玩家横条上的选择状态
        if (ms_SelectedPlayerID ~= -1) then
            local ribbonEntry = ms_LeaderIDToRibbonEntry[ms_SelectedPlayerID];
            if (ribbonEntry ~= nil) then -- 可以选择已淘汰的玩家，所以要检查是否为nil
                ribbonEntry.SelectedBackground:SetHide( false );
            end

            -- 根据玩家ID更新玩家面板和头部信息
            if(ms_SelectedPlayerID ~= ms_LocalPlayerID) then
                PopulatePlayerPanelHeader(ms_PlayerPanel, ms_SelectedPlayer);
                PopulatePlayerPanel(ms_PlayerPanel, ms_SelectedPlayer);
            end
            Controls.LeaderAnchor:SetHide(false);

            -- 根据当前模式或刷新标志，显示/隐藏相应的控制元素或设置相应数据
            if (ms_currentViewMode ~= mode or refresh) then
                if (mode == CONVERSATION_MODE) then
                    SetConversationMode(ms_SelectedPlayer);
                elseif (mode == CINEMA_MODE) then
                    -- 如果切换到电影模式，需要等待淡出完成后更新显示
                    if (ContextPtr:IsHidden()) then
                        -- 如果上下文被隐藏，那么在上下文显示时直接进入电影模式
                        m_cinemaMode = true;
                    else
                        StartFadeOut();
                    end
                elseif (mode == OVERVIEW_MODE) then
                    -- 更新概览模式的显示
                    Controls.ConversationContainer:SetHide(true);
                    Controls.OverviewContainer:SetHide(false);
                    Controls.VoiceoverTextContainer:SetHide(true);
                    if (not m_firstOpened) then
                        -- 如果是第一次打开，设置动画速度和暂停时间
                        Controls.AlphaIn:SetSpeed(3);
                        Controls.SlideIn:SetSpeed(3);
                        Controls.AlphaIn:SetPauseTime(0);
                        Controls.SlideIn:SetPauseTime(0);
                        Controls.SlideIn:SetBeginVal(-20,0);
                    end
                    Controls.AlphaIn:SetToBeginning();
                    Controls.SlideIn:SetToBeginning();
                    Controls.AlphaIn:Play();
                    Controls.SlideIn:Play();
                    UI.PlaySound("UI_Diplomacy_Open_Long");
                end
                ms_currentViewMode = mode;
            end
            ShowLeader(ms_SelectedPlayer);
        end
    end
    -- 如果选择的玩家ID不是本地玩家ID，则触发LeaderSelect事件,刷新LeaderSelect
    if (isDifferentPlayer and ms_SelectedPlayerID ~= ms_LocalPlayerID) then
        LuaEvents.DiploScene_LeaderSelect(playerID);
    end
    m_firstOpened = false;
    local w,h = UIManager:GetScreenSizeVal();

    -- 如果选择的玩家是本地玩家，设置特殊显示
    if(ms_SelectedPlayerID == ms_LocalPlayerID) then
        Controls.NameFade:SetHide(false);
        local playerConfig = PlayerConfigurations[ms_LocalPlayerID];
        if (playerConfig ~= nil) then
            -- 设置玩家名称和文明名称
            Controls.NameFade:SetToBeginning();
            Controls.NameFade:Play();
            Controls.NameSlide:SetToBeginning();
            Controls.NameSlide:Play();
            local leaderDesc = playerConfig:GetLeaderName();
            Controls.PlayerNameText:LocalizeAndSetText( Locale.ToUpper( Locale.Lookup(leaderDesc)));
            Controls.CivNameText:LocalizeAndSetText( Locale.ToUpper( Locale.Lookup(playerConfig:GetCivilizationDescription())));
            SetUniqueCivLeaderData(); -- 设置独特的文明和领袖数据
        end
        Controls.PlayerContainer:SetHide(true);
        UI.SetLeaderPosition(Controls.LeaderAnchor:GetScreenOffset());
        LuaEvents.DiploScene_CinemaSequence(ms_SelectedPlayerID);
        print("pikOffset1: ", Controls.LeaderAnchor:GetScreenOffset())
    else
        Controls.NameFade:SetHide(true);
        Controls.PlayerContainer:SetHide(false);
        Controls.LeaderAnchor:SetOffsetX(w - (w/3.5));
        local H = Controls.LeaderAnchor:GetOffsetY();
        print("pikOffset2H: ", H)
        --Controls.LeaderAnchor:SetOffsetY(H+850);
        UI.SetLeaderPosition(Controls.LeaderAnchor:GetScreenOffset());
        print("pikOffset2: ", Controls.LeaderAnchor:GetScreenOffset())
    end
end

-- SetUniqueCivLeaderData: 设置当前文明和领袖的特色信息显示
function SetUniqueCivLeaderData()
    -- 重置图标和文本实例
    ms_uniqueIconIM:ResetInstances();
    ms_uniqueTextIM:ResetInstances();

    local playerConfig        :table = PlayerConfigurations[ms_LocalPlayerID];
    local civType    :string = playerConfig:GetCivilizationTypeName();
    local leaderType    :string = playerConfig:GetLeaderTypeName();
    local uniqueAbilities;
    local uniqueUnits;
    local uniqueBuildings;
    -- 获取领袖和文明的特色能力、单位和建筑
    uniqueAbilities, uniqueUnits, uniqueBuildings = GetLeaderUniqueTraits( leaderType, true );
    local CivUniqueAbilities, CivUniqueUnits, CivUniqueBuildings = GetCivilizationUniqueTraits( civType, true );
    
    -- 合并特色能力、单位和建筑列表
    for i,v in ipairs(CivUniqueAbilities)    do table.insert(uniqueAbilities, v) end
    for i,v in ipairs(CivUniqueUnits)        do table.insert(uniqueUnits, v)        end
    for i,v in ipairs(CivUniqueBuildings)    do table.insert(uniqueBuildings, v) end

    -- 为每个非"NONE"名称的特色能力、单位和建筑生成显示内容
    for _, item in ipairs(uniqueAbilities) do
        if item.Name and item.Name ~= "NONE"  then
            local instance:table = {};
            instance = ms_uniqueTextIM:GetInstance();
            local headerText:string = Locale.ToUpper(Locale.Lookup( item.Name )); 
            instance.Header:SetText( headerText );
            instance.Description:SetText( Locale.Lookup( item.Description ) );
        end
    end

    local size:number = SIZE_BUILDING_ICON;

    for _, item in ipairs(uniqueUnits) do
        local instance:table = {};
        instance = ms_uniqueIconIM:GetInstance();
        iconAtlas = "ICON_"..item.Type;
        instance.Icon:SetIcon(iconAtlas);
        instance.TextStack:SetOffsetX( size + 4 );
        local headerText:string = Locale.ToUpper(Locale.Lookup( item.Name ));
        instance.Header:SetText( headerText );
        instance.Description:SetText(Locale.Lookup(item.Description));
    end


    for _, item in ipairs(uniqueBuildings) do
        local instance:table = {};
        instance = ms_uniqueIconIM:GetInstance();
        instance.Icon:SetSizeVal(38,38);
        iconAtlas = "ICON_"..item.Type;
        instance.Icon:SetIcon(iconAtlas);
        instance.TextStack:SetOffsetX( size + 4 );
        local headerText:string = Locale.ToUpper(Locale.Lookup( item.Name ));
        instance.Header:SetText( headerText );
        instance.Description:SetText(Locale.Lookup(item.Description));
    end

    -- 计算并更新特色信息堆叠的大小
    Controls.UniqueInfoStack:CalculateSize();
end
-- ===========================================================================
-- 当玩家被选中时触发的函数。
-- 参数 playerID 被选中的玩家ID。
function OnPlayerSelected(playerID)
    print("OnPlayerSelected: ", playerID)
    -- 检查是否有外交能力，或者当前玩家是本地玩家且有查看自身外交信息的能力。
    if (HasCapability("CAPABILITY_DIPLOMACY") or (playerID == Game.GetLocalPlayer() and HasCapability("CAPABILITY_DIPLOMACY_VIEW_SELF"))) then
        ResetPlayerPanel(); -- 重置玩家面板。
        SelectPlayer(playerID); -- 选择玩家。
    end
end

-- ===========================================================================
-- 为外交横幅填充信息。
-- 参数 diplomacyRibbon 外交横幅的表格，用于存放外交信息。
function PopulateDiplomacyRibbon(diplomacyRibbon : table)
    -- 检查本地玩家是否存在。
    if (ms_LocalPlayer ~= nil) then
        local pLocalPlayerDiplomacy = ms_LocalPlayer:GetDiplomacy(); -- 获取本地玩家的外交信息。
        ms_DiplomacyRibbonLeaderIM:ResetInstances(); -- 重置外交横幅上的领导者图标实例。
        ms_LeaderIDToRibbonEntry = {}; -- 清空玩家ID到横幅条目的映射。
        local currentCoordinateY = 32; -- 初始化当前Y坐标。
        local coordinateOffsetIncrement = 64; -- 坐标增量。

        -- 设置顾问图标（当前未使用）。

        -- 为本地玩家在顶部添加一个条目。
        local oLeaderIcon     :object = LeaderIcon:GetInstance(ms_DiplomacyRibbonLeaderIM, diplomacyRibbon.Leaders);
        ms_LeaderIDToRibbonEntry[ms_LocalPlayerID] = oLeaderIcon;
        PopulateLeader(oLeaderIcon, ms_LocalPlayer); -- 填充本地玩家的领导信息。

        -- 检查是否有重复的领导者，并进行追踪。
        local isUniqueLeader: table = {}; -- 用于追踪领导者的唯一性。
        local aPlayers = PlayerManager.GetAliveMajors(); -- 获取所有活着的主要玩家。
        for _, pPlayer in ipairs(aPlayers) do
            local playerID:number = pPlayer:GetID();
            if(playerID ~= ms_LocalPlayerID) then
                local leaderName:string = PlayerConfigurations[playerID]:GetLeaderTypeName();
                if (isUniqueLeader[leaderName] == nil) then
                    isUniqueLeader[leaderName] = true;
                else
                    isUniqueLeader[leaderName] = false;
                end
            end
        end

        -- 为所有已知玩家（仅限主要玩家）添加条目。
        local aPlayers = PlayerManager.GetAliveMajors();
        for _, pPlayer in ipairs(aPlayers) do
            if (pPlayer:GetID() ~= ms_LocalPlayerID and pLocalPlayerDiplomacy:HasMet(pPlayer:GetID())) then
                local oLeaderIcon :object = LeaderIcon:GetInstance(ms_DiplomacyRibbonLeaderIM, diplomacyRibbon.Leaders);
                ms_LeaderIDToRibbonEntry[pPlayer:GetID()] = oLeaderIcon;
                -- 保存当前坐标，以便于之后自动滚动到这一位置
                m_LeaderCoordinates[pPlayer:GetID()] = currentCoordinateY;
                currentCoordinateY = currentCoordinateY + coordinateOffsetIncrement;
                local leaderName:string = PlayerConfigurations[pPlayer:GetID()]:GetLeaderTypeName();
                if(isUniqueLeader[leaderName] ~= nil) then
                    PopulateLeader(oLeaderIcon, pPlayer, isUniqueLeader[leaderName]);
                else
                    PopulateLeader(oLeaderIcon, pPlayer);
                end
            end
        end

        -- 重建堆栈并调整大小以适应所有领导者。
        diplomacyRibbon.Leaders:CalculateSize();
        diplomacyRibbon.LeaderRibbonScroll:CalculateSize();

        -- 如果所有领导者的信息不能完全显示在滚动条内，调整外交横幅的偏移来适应滚动条。
        if(diplomacyRibbon.Leaders:GetSizeY() > diplomacyRibbon.LeaderRibbonScroll:GetSizeY()) then
            diplomacyRibbon.Root:SetOffsetX(15);
        end
        local offsetX = DIPLOMACY_RIBBON_OFFSET + diplomacyRibbon.Root:GetOffsetX();
        Controls.PlayerContainer:SetOffsetX(offsetX);
        Controls.NameFade:SetOffsetX(offsetX);
        --Controls.OverviewContainer:SetOffsetX(offsetX);
    end
end

-- ===========================================================================
-- 初始化参与视图的玩家。
-- ===========================================================================
-- 该函数设置与游戏视图中相关的玩家信息，包括本地玩家、另一方玩家以及初始化外交会话等相关信息。
-- 无参数
-- 返回值: 
--   如果成功初始化玩家信息则返回true，否则返回false。
function SetupPlayers()

    -- 设置一些全局变量以便于访问

    -- 存储本地玩家的ID。注意，每次显示时我们都这样做，因为本地玩家的ID可能会改变，所以不要在一次性构造函数中做这个操作。
    ms_LocalPlayerID = Game.GetLocalPlayer();

    -- 检查是否获取到了本地玩家ID
    if (ms_LocalPlayerID == -1) then
        -- 如果没有获取到本地玩家ID，则尝试获取本地观察者ID
        ms_LocalPlayerID = Game.GetLocalObserver();
        -- 如果连本地观察者的ID都没有获取到，则退出函数
        if (ms_LocalPlayerID == -1) then
            ms_LocalPlayer = nil
            return false;
        end
        -- 如果本地玩家是观察者，则将其ID设置为0，领导类型ID设置为-1
        if (ms_LocalPlayerID == PlayerTypes.OBSERVER) then
            ms_LocalPlayerID = 0;
            ms_LocalPlayerLeaderID = -1;
        end
    else
        -- 如果成功获取到本地玩家ID，则获取其领导类型ID
        ms_LocalPlayerLeaderID = PlayerConfigurations[ms_LocalPlayerID]:GetLeaderTypeID();
    end

    -- 获取本地玩家对象
    ms_LocalPlayer = Players[ms_LocalPlayerID];

    -- 如果已经设置了其他玩家的ID
    if (ms_OtherPlayerID ~= -1) then
        -- 获取其他玩家对象
        ms_OtherPlayer = Players[ms_OtherPlayerID];
        -- 查找本地玩家和另一方玩家之间的开放会话ID
        local sessionID = DiplomacyManager.FindOpenSessionID(ms_LocalPlayerID, ms_OtherPlayer:GetID());
        -- 如果找到了会话ID，则获取会话信息，并设置发起玩家ID
        if (sessionID ~= nil) then
            local sessionInfo = DiplomacyManager.GetSessionInfo(sessionID);
            ms_InitiatedByPlayerID = sessionInfo.FromPlayer;
        end
    else
        -- 如果没有设置其他玩家ID，则初始化其他玩家为nil，并设置发起玩家ID为本地玩家ID
        ms_OtherPlayer = nil;
        ms_InitiatedByPlayerID = ms_LocalPlayerID;
    end

    -- 判断是AI还是人类玩家发起了此次交互
    if (ms_InitiatedByPlayerID == ms_OtherPlayerID) then
        -- 设置界面中拒绝交易的按钮文本（未展示具体设置方法）
        ms_LastIncomingDealProposalAction = DealProposalAction.PROPOSED;
    else
        -- 设置界面中退出交易的按钮文本（未展示具体设置方法）
        ms_LastIncomingDealProposalAction = DealProposalAction.PENDING;
    end

    return true;
end
-- ===========================================================================
-- 显示指定的领袖。
-- 这是一个异步操作，当领袖加载完成时会发送事件。
-- ===========================================================================
-- 显示领袖
-- 参数:
-- player : table - 玩家信息表
function ShowLeader(player : table )
    local leaderName = PlayerConfigurations[ player:GetID() ]:GetLeaderTypeName();
    print("ShowLeader: ", leaderName);
    if (leaderName ~= ms_showingLeaderName) then
        -- 如果当前显示的领袖与要显示的领袖不同，开始加载新领袖
        ms_showingLeaderName = leaderName;
        ms_LastDealResponseAnimation = nil;
        ms_bLeaderShowRequested = true;
        LeaderSupport_Initialize(); -- 初始化领袖支持
        Events.ShowLeaderScreen(leaderName, player:GetID() == Game.GetLocalPlayer()); -- 发送显示领袖屏幕的事件
        Controls.FallbackLeaderImage:SetHide(true); -- 隐藏回退领袖图像，直到加载完成
        Controls.LeaderAlpha:SetToBeginning();
        Controls.LeaderAlpha:Play(); -- 播放领袖透明度动画
    else
        if (not ms_bLeaderShowRequested) then
            -- 如果已经在显示该领袖，直接调用完成回调函数
            LeaderSupport_ClearInitialAnimationState(); -- 清除初始动画状态
            OnLeaderLoaded(); -- 调用领袖加载完成的回调
        end
    end
end

-- ===========================================================================
-- 初始化视图。
-- 如果视图未初始化，则进行初始化操作，包括设置领袖图像控制、调整视图位置等。
function InitializeView()
    print("InitializeView");
    if (not ms_bIsViewInitialized) then
        -- 添加事件监听，以在领袖屏幕加载完成和动画完成后进行处理
        Events.LeaderScreenFinishedLoading.Add(    OnLeaderLoaded );
        Events.LeaderAnimationComplete.Add( OnLeaderAnimationComplete );
        LeaderSupport_Initialize(); -- 初始化领袖支持
        -- 设置显示领袖的控制元素
        UI.SetLeaderImageControl( Controls.FallbackLeaderImage );
        local w,h = UIManager:GetScreenSizeVal(); -- 获取屏幕尺寸
        -- 调整领袖显示位置
        -- Controls.LeaderAnchor:SetOffsetX(w - (w/4));
        Controls.LeaderAnchor:SetOffsetX(w - (w/4));
        Controls.LeaderAnchor:SetOffsetY(h/2);
        -- 设置领袖在屏幕上的位置
        UI.SetLeaderPosition(Controls.LeaderAnchor:GetScreenOffset());
        print("pikOffset3: ", Controls.LeaderAnchor:GetScreenOffset())

        if not m_LiteMode then
            if m_eventID == 0 then
                m_eventID = UI.ReferenceCurrentEvent(); -- 获取当前事件ID，锁定引擎
            else
                UI.DataError("Attempt to obtain lock ID but m_eventID already has ID: "..tostring(m_eventID));
            end        
        end

        LuaEvents.DiplomacyActionView_HideIngameUI(); -- 发送隐藏游戏内UI的信号
        ms_bIsViewInitialized = true;
    end
end
local LeaderHide = true;
function HideFLeaderControls()
    local w,h = UIManager:GetScreenSizeVal(); -- 获取屏幕尺寸
    if LeaderHide then
        LeaderHide = false;
        Controls.LeaderAnchor:SetOffsetY(-9999);
        -- 设置领袖在屏幕上的位置
        UI.SetLeaderPosition(Controls.LeaderAnchor:GetScreenOffset());
        return
    end
    LeaderHide = true;
    Controls.LeaderAnchor:SetOffsetY(h/2);
    -- 设置领袖在屏幕上的位置
    UI.SetLeaderPosition(Controls.LeaderAnchor:GetScreenOffset());
end
LuaEvents.PKHideFiraxisLeaderControls.Add(HideFLeaderControls);
-- ===========================================================================
-- 功能描述: 若视图未被干净地初始化，则返回true。
-- ===========================================================================
-- 参数列表: 无
-- 返回值: 
--   - true: 如果视图干净地未初始化。
--   - false: 如果视图已经初始化，或者在游戏开始后几乎立即关闭而没有显示外交。
function UninitializeView()

    if ms_bIsViewInitialized==false then
        -- 如果游戏开始后几乎立即关闭而没有显示外交，则返回false。
        return false;
    end

    ContextPtr:SetHide(true); -- 隐藏当前视图

    -- 如果领袖动画可见，或者已请求显示领导者，则隐藏领导者屏幕
    if (LeaderSupport_IsLeaderVisible() or ms_bLeaderShowRequested) then
        Events.HideLeaderScreen();
    end

    -- 重置相关状态和变量
    ms_LastDealResponseAnimation = nil;
    ms_bLeaderShowRequested = false;
    ms_bIsViewInitialized = false;
    ms_bShowingDeal = false;
    ms_ActiveSessionID = nil;
    ms_currentViewMode = -1;
    m_cinemaMode = false;

    -- 重置玩家相关变量
    ms_OtherPlayer = nil;
    ms_OtherPlayerID = -1;

    ms_SelectedPlayer = nil;
    ms_SelectedPlayerID    = -1;
    ms_SelectedPlayerLeaderTypeName = nil;

    -- 重置领导者名称显示和请求状态
    ms_showingLeaderName = "";
    ms_bLeaderShowRequested = false;

    -- 重置界面元素动画状态
    Controls.LeaderResponse_Alpha:SetToBeginning();
    Controls.ConversationSelection_Alpha:SetToBeginning();
    Controls.LeaderResponse_Slide:SetToBeginning();
    Controls.ConversationSelection_Slide:SetToBeginning();
    Controls.AlphaIn:SetSpeed(2);
    Controls.SlideIn:SetSpeed(2);
    Controls.AlphaIn:SetPauseTime(.4);
    Controls.SlideIn:SetPauseTime(.4);
    Controls.SlideIn:SetBeginVal(-200,0);

    -- 根据LiteMode状态，释放或重置事件ID
    if m_LiteMode then
        m_LiteMode = false;
        ms_PlayerPanel.RootOptionStack:SetHide(false);
    else
        UI.ReleaseEventID( m_eventID );    
        m_eventID = 0;
    end

    return true;
end
-- ===========================================================================
-- 处理显示请求，该函数应该仅由外交声明处理器调用。
-- ===========================================================================
-- 参数:
--   playerID - 请求显示的玩家ID
function OnOpenDiplomacyActionViewLite(playerID)
    m_LiteMode = true;
    OnOpenDiplomacyActionView(playerID);
end


-- ===========================================================================
-- 打开外交动作视图，用于显示和处理与指定玩家的外交关系。
-- 参数:
--   otherPlayerID - 需要显示外交动作视图的其他玩家ID
-- 注:
--   如果当前玩家具有外交能力，或当玩家为本地玩家且具有查看自身外交能力时，函数将执行。
--   该函数会初始化视图，并根据指定玩家ID填充外交选项。
function OnOpenDiplomacyActionView(otherPlayerID)
    print("OnOpenDiplomacyActionView: ", otherPlayerID);
    -- 检查当前玩家是否有执行外交操作的能力
    if (HasCapability("CAPABILITY_DIPLOMACY") or (otherPlayerID == Game.GetLocalPlayer() and HasCapability("CAPABILITY_DIPLOMACY_VIEW_SELF"))) then

        -- 设置目标玩家ID，若无则设置为-1
        if (otherPlayerID ~= nil) then
            ms_OtherPlayerID = otherPlayerID;
            ms_SelectedPlayerID = otherPlayerID;
        else
            ms_OtherPlayerID = -1;
            ms_SelectedPlayerID = -1;
        end

        InitializeView();    -- 初始化视图
        m_firstOpened = true;    -- 标记首次打开

        -- 配置玩家并填充外交选项
        if (SetupPlayers()) then

            PopulateDiplomacyRibbon(ms_DiplomacyRibbon);    -- 填充外交带

            -- 选择显示的玩家，若无则选择本地玩家
            if (ms_OtherPlayer ~= nil) then
                SelectPlayer(ms_OtherPlayer:GetID(), OVERVIEW_MODE);
            else
                SelectPlayer(ms_LocalPlayer:GetID(), OVERVIEW_MODE);
            end

            -- 如果有指定的玩家ID，滚动到相应节点；否则，滚动到顶部
            if(ms_OtherPlayerID ~= 0) then
                ScrollToNode(ms_OtherPlayerID);
            else
                ms_DiplomacyRibbon.LeaderRibbonScroll:SetScrollValue(0);    -- 滚动到顶部
            end
        end    
    end

end

-- ===========================================================================
-- 当领导者动画加载完成时的处理函数
-- 参数 animationName: 完成的动画名称
function OnLeaderAnimationComplete(animationName : string)
    print("OnLeaderAnimationComplete: ", animationName);
    -- 如果视图初始化尚未完成，则直接返回
    if (not ms_bIsViewInitialized) then
        return;
    end

    -- 更新动画队列
    LeaderSupport_UpdateAnimationQueue();
    -- 遍历语音动画列表，若找到匹配的动画，则启动淡出效果
    for _, voiceoverAnimationName in ipairs(VOICEOVER_SUPPORT) do
        if (voiceoverAnimationName == animationName) then
            StartFadeOut();
            break;
        end
    end
end

-- ===========================================================================
-- 设置交易动画
-- 参数 animationName: 动画名称
-- 参数 useMood: 是否根据情绪使用不同的动画
function OnSetDealAnimation(animationName : string, useMood : boolean)

    -- 当前处于交易模式
    if (ms_currentViewMode == DEAL_MODE) then
        -- 如果需要根据情绪使用动画
        if (useMood ~= nil and useMood == true) then
            -- 获取玩家情绪
            local ePlayerMood = DiplomacySupport_GetPlayerMood(ms_SelectedPlayer, ms_LocalPlayerID);
        
            -- 根据玩家情绪设置动画名称前缀
            if (ePlayerMood == DiplomacyMoodTypes.HAPPY) then 
                animationName = "HAPPY_" .. animationName;
            elseif (ePlayerMood == DiplomacyMoodTypes.NEUTRAL) then
                animationName = "NEUTRAL_" .. animationName;
            elseif (ePlayerMood == DiplomacyMoodTypes.UNHAPPY) then
                animationName = "UNHAPPY_" .. animationName;
            end
        end
        -- 将动画加入队列
        LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, animationName );
    end
end


-- ===========================================================================
-- 显示影院模式
function ShowCinemaMode()
    -- 获取屏幕大小，设置对话容器的隐藏状态
    local w,h = UIManager:GetScreenSizeVal();
    Controls.ConversationContainer:SetHide(m_cinemaMode);

    -- 如果不是正在从黑色背景淡出，则清除初始动画状态，准备播放下一个动画
    if (not Controls.BlackFadeAnim:IsReversing()) then
        LeaderSupport_ClearInitialAnimationState();
    end
    -- 设置领导者场景的特殊显示效果
    Controls.LeaderAnchor:SetOffsetX(w - (w/2.5));
    UI.SetLeaderPosition(Controls.LeaderAnchor:GetScreenOffset());
    print("pikOffset4: ", Controls.LeaderAnchor:GetScreenOffset())
    -- 根据选定玩家的情绪，设置动画和场景效果
    local ePlayerMood = DiplomacySupport_GetPlayerMood(ms_SelectedPlayer, ms_LocalPlayerID);
    LeaderSupport_QueueAnimationSequence( ms_OtherLeaderName, m_currentLeaderAnim, ePlayerMood );
    LeaderSupport_QueueSceneEffect( m_currentSceneEffect );
    -- 触发影院序列相关的Lua事件
    LuaEvents.DiploScene_CinemaSequence(ms_SelectedPlayerID);
    -- 设置显示和隐藏的容器
    Controls.OverviewContainer:SetHide(true);
    Controls.VoiceoverTextContainer:SetHide(false);
    -- 设置语音文本并播放动画
    Controls.VoiceoverText:SetText(m_voiceoverText);
    Controls.VoiceoverText_Alpha:SetToBeginning();
    Controls.VoiceoverText_Alpha:Play();
    Controls.VoiceoverText_Slide:SetToBeginning();
    Controls.VoiceoverText_Slide:Play();
end

-- ===========================================================================
-- 切换电影模式
-- 该函数用于根据当前电影模式的状态进行切换，并执行相应的动画和界面更新操作。
function ToggleCinemaMode()
    if (m_bCloseSessionOnFadeComplete == true) then
        -- 当前正在淡出以关闭会话
        m_bCloseSessionOnFadeComplete = false;
        m_cinemaMode = false;
        DiplomacyManager.CloseSession( ms_ActiveSessionID );
    else
        -- 切换电影模式的状态
        m_cinemaMode = not m_cinemaMode;
        local w,h = UIManager:GetScreenSizeVal();
        Controls.ConversationContainer:SetHide(m_cinemaMode);

        -- 如果当前淡入黑屏动画未反向播放，则准备下一个动画
        if (not Controls.BlackFadeAnim:IsReversing()) then
            LeaderSupport_ClearInitialAnimationState();
        end

        -- 根据电影模式的新状态，执行相应的操作
        if (m_cinemaMode) then
            -- 进入电影模式时的处理
            ShowCinemaMode();
        else
            -- 退出电影模式时的处理
            Controls.LeaderAnchor:SetOffsetX(w - (w/3));
            UI.SetLeaderPosition(Controls.LeaderAnchor:GetScreenOffset());
            print("pikOffset5: ", Controls.LeaderAnchor:GetScreenOffset())
            SelectPlayer(ms_OtherPlayerID, CONVERSATION_MODE, false, true);
            Controls.VoiceoverTextContainer:SetHide(true);    
            local ePlayerMood = DiplomacySupport_GetPlayerMood(ms_SelectedPlayer, ms_LocalPlayerID);
            -- 根据玩家情绪设置适当的闲置动画
            if (ePlayerMood == DiplomacyMoodTypes.HAPPY) then 
                LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "HAPPY_IDLE" );
            elseif (ePlayerMood == DiplomacyMoodTypes.NEUTRAL) then
                LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "NEUTRAL_IDLE" );
            elseif (ePlayerMood == DiplomacyMoodTypes.UNHAPPY) then
                LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "UNHAPPY_IDLE" );
            end
        end
        -- 反向播放淡入淡出动画
        Controls.BlackFadeAnim:Reverse();
    end
end
-- 注册一个结束回调，以便在淡入/淡出动画结束时切换电影模式
Controls.FadeTimerAnim:RegisterEndCallback(ToggleCinemaMode);
-- ===========================================================================
-- 当黑色淡出动画结束时的处理函数
function OnBlackFadeEnd()
    -- 如果淡出后的透明度为0，则隐藏控制元素
    if (Controls.BlackFadeAnim:GetAlpha() == 0) then
        Controls.BlackFade:SetHide(true);
    end
end
Controls.BlackFadeAnim:RegisterEndCallback(OnBlackFadeEnd);

-- ===========================================================================
-- 领导人加载完成时的处理函数
-- 该函数没有参数和返回值
function OnLeaderLoaded()
    ms_bLeaderShowRequested = false;
    print("ppkOnLeaderLoaded")

    -- 如果视图尚未初始化，则不执行任何操作
    if (not ms_bIsViewInitialized) then
        return;
    end
    -- 当领导者加载完成时调用播放领袖动画
    LeaderSupport_OnLeaderLoaded();
    local bDoAudio = false;
    
    -- 如果当前屏幕被隐藏，则显示屏幕并进行音频处理
    if (ContextPtr:IsHidden()) then
        ContextPtr:SetHide(false);
        bDoAudio = true;
        m_lastLeaderPlayedMusicFor = -1;
        m_curModderMusic = -1;
        m_bCurrentMusicIsModder = false;
    end
    Controls.FallbackLeaderImage:SetHide(false);

    -- 如果当前没有活跃的会话ID，根据玩家情绪设置闲置动画
    if (ms_ActiveSessionID == nil) then
        bDoAudio = true;
        local ePlayerMood = DiplomacySupport_GetPlayerMood(ms_SelectedPlayer, ms_LocalPlayerID);
        print("ppkOnLeaderLoaded2", ePlayerMood)
        -- 根据玩家对我们的情绪选择相应的动画
        ePlayerMood = GetModifiedMood(ePlayerMood)
        --[
        if (ePlayerMood == DiplomacyMoodTypes.HAPPY) then 
            LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "HAPPY_IDLE" );
        elseif (ePlayerMood == DiplomacyMoodTypes.NEUTRAL) then    
            LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "NEUTRAL_IDLE" );
        elseif (ePlayerMood == DiplomacyMoodTypes.UNHAPPY) then
            LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "UNHAPPY_IDLE" );
        end
        --]]
        
        --[
            
        --LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "FIRST_MEET" );
        --Controls.FallbackLeaderImage:SetTexture("Controls_GradientSmall")
        --Controls.FallbackLeaderImage:SetIcon("ICON_CREW_MORALE_GOOD")
      --  ContextPtr:SetHide(false);
       Controls.FallbackLeaderImage:SetHide(false);
       -- Controls.LeaderAnchor:SetHide(false);
       -- Controls.FallbackLeaderImage:SetSizeX(500)
        --Controls.FallbackLeaderImage:SetSizeY(500)
        --Controls.LeaderAnchor:SetSizeX(500)
        --Controls.LeaderAnchor:SetSizeY(500)
      --  print("Controls.FallbackLeaderImage: ", Controls.FallbackLeaderImage:GetTexture())
      print("Controls.FallbackLeaderImageXY: ", Controls.FallbackLeaderImage:GetSizeX(),Controls.FallbackLeaderImage:GetSizeY())
      --  print("Controls.LeaderAnchorXY: ", Controls.LeaderAnchor:GetSizeX(),Controls.LeaderAnchor:GetSizeY())
        --LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, "FIRST_MEET" );
        --]]
    end

    -- 如果领导人的ID不同，则更改音频（针对不同的领导人播放不同的音乐）
    if (m_lastLeaderPlayedMusicFor ~= ms_OtherLeaderID) then
        bDoAudio = true;
    end

    -- 如果需要进行音频处理，则进行相应的音频操作
    if (bDoAudio == true) then
        -- 停止可能的干扰音源
        UI.PlaySound("Stop_Speech_Civics");
        UI.PlaySound("Stop_Speech_Tech");
        UI.PlaySound("Stop_Speech_GreatWriting");
        UI.PlaySound("Stop_Speech_NaturalWonders");

        -- 暂停mod音乐
        if (not m_bIsModPaused) then
            UI.PauseModCivMusic();
            m_bIsModPaused = true;
        end

        -- 如果领导人ID不匹配
        if (m_lastLeaderPlayedMusicFor ~= ms_OtherLeaderID) then
            -- 如果之前播放的是mod音乐，停止该音乐
            if m_bCurrentMusicIsModder then
                UI.StopModCivLeaderMusic(m_curModderMusic);
            end

            -- 总是在这里降低环境音
            UI.SetSoundStateValue("Game_Views", "Leader_Screen");

            -- 如果领导人ID不匹配，总是重新播放mod音乐
            if (UI.ShouldCivPlayModMusic(ms_OtherCivilizationID)) then
                UI.PlaySound("Stop_Leader_Music");
                UI.PlayModCivLeaderMusic(ms_OtherID);
                m_lastLeaderPlayedMusicFor = ms_OtherLeaderID;
                m_curModderMusic = ms_OtherID;
                m_bCurrentMusicIsModder = true;
            else
                -- 如果新的领导人也是mod文明，处理这种情况
                if (UI.GetCivilizationSoundSwitchValueByLeader(m_lastLeaderPlayedMusicFor) ~= UI.GetCivilizationSoundSwitchValueByLeader(ms_OtherLeaderID)) then
                    UI.SetSoundSwitchValue("LEADER_SCREEN_CIVILIZATION", UI.GetCivilizationSoundSwitchValueByLeader(ms_OtherLeaderID));
                    UI.SetSoundSwitchValue("Game_Location", UI.GetNormalEraSoundSwitchValue(ms_OtherID));
                    UI.PlaySound("Play_Leader_Music");
                    m_lastLeaderPlayedMusicFor = ms_OtherLeaderID;
                end
                m_bCurrentMusicIsModder = false;
            end
        end
    end
end
-- ===========================================================================
-- 为领袖加载时选择视觉情绪提供一个额外逻辑的注入点
function GetModifiedMood( inMood  )
    -- 当前玩家与被选择的玩家相同时，为Alt-Catherine提供特定的情绪设定
    if (ms_LocalPlayerID == ms_SelectedPlayerID) then
        local localPlayer = Game.GetLocalPlayer()
        local playerConfig:table = PlayerConfigurations[localPlayer]
        local leaderTypeName:string = playerConfig:GetLeaderTypeName()    
        if (leaderTypeName == "LEADER_CATHERINE_DE_MEDICI_ALT" ) then 
            inMood = DiplomacyMoodTypes.HAPPY -- 当玩家控制的是Alt-Catherine时，让她看起来很开心
        end
    end

    return inMood -- 返回可能被修改的情绪值
end
-- ===========================================================================
-- 根据声明类型名称获取声明处理函数
function GetStatementHandler(statementTypeName : string)
    local handler = StatementHandlers[statementTypeName]; -- 尝试从声明处理器映射中获取特定的处理器
    if (handler == nil) then
        handler = DefaultHandlers; -- 如果没有找到特定的处理器，则使用默认处理器
    end
    return handler; -- 返回找到的或默认的声明处理函数
end
-- ===========================================================================
-- 当玩家之间建立和平关系时触发的函数
-- eActingPlayer: 主动发起和平提议的玩家ID
-- eReactingPlayer: 对和平提议做出反应的玩家ID
function OnDiplomacyMakePeace(eActingPlayer :number, eReactingPlayer :number)
    local localPlayer = Game.GetLocalPlayer();
    if(ms_SelectedPlayerID ~= -1
        and (localPlayer == eActingPlayer or localPlayer == eReactingPlayer)
        and (ms_SelectedPlayerID == eActingPlayer or ms_SelectedPlayerID == eReactingPlayer)) then
            -- 刷新玩家面板，以更新选项
            PopulatePlayerPanelHeader(ms_PlayerPanel, ms_SelectedPlayer);
            PopulatePlayerPanel(ms_PlayerPanel, ms_SelectedPlayer);
    end
end

------------------------------------------------------------------------------
-- 检查是否还有待处理的会话
-- sessionID: 当前会话的ID
-- 返回值: 如果存在待处理的会话则返回true，否则返回false
function HasNextQueuedSession(sessionID)
    if (ms_ActiveSessionID ~= nil and ms_ActiveSessionID == sessionID and not m_isInHotload) then

        if (ms_InitiatedByPlayerID == ms_OtherPlayerID) then
            -- 如果是对方玩家发起的接触，仅退出
            if (DiplomacyManager.HasQueuedSession(ms_LocalPlayerID)) then
                return true;
            end
        end
    end

    return false;
end
------------------------------------------------------------------------------
-- 当外交会话关闭时触发的函数
-- sessionID: 关闭的会话的ID
function OnDiplomacySessionClosed(sessionID)
    -- 如果视图尚未初始化，则不进行任何操作
    if (not ms_bIsViewInitialized) then
        ms_ActiveSessionID = nil;
        return;
    end

    if (ms_ActiveSessionID ~= nil) then
        if ( ms_ActiveSessionID == sessionID and not m_isInHotload) then
            ms_ActiveSessionID = nil;

            if (ms_currentViewMode == DEAL_MODE) then
                -- 如果当前视图是交易模式，则重置玩家面板并隐藏交易弹窗
                ResetPlayerPanel();
                LuaEvents.DiploPopup_HideDeal();
                ms_bShowingDeal = false;
            end

            if (ms_InitiatedByPlayerID == ms_OtherPlayerID) then
                -- 如果会话是由对方玩家发起的，则直接退出
                Close();
            else
                -- 如果会话是由本地玩家发起的，则返回概览视图
                SelectPlayer(ms_OtherPlayerID, OVERVIEW_MODE);
                PopulateDiplomacyRibbon(ms_DiplomacyRibbon);
            end
        end
    else
        -- 如果收到了会话关闭的通知，但当前没有处于任何会话中
        -- 可能需要刷新概览屏幕，因为关闭的会话可能是玩家向另一个玩家发送的外交请求
        if (ms_currentViewMode == OVERVIEW_MODE) then
            local sessionInfo = DiplomacyManager.GetSessionInfo(sessionID);
            if (sessionInfo ~= nil) then
                if (sessionInfo.FromPlayer == Game.GetLocalPlayer() or sessionInfo.ToPlayer == Game.GetLocalPlayer()) then
                    SelectPlayer(ms_SelectedPlayerID, OVERVIEW_MODE, true);
                end
            end
            PopulateDiplomacyRibbon(ms_DiplomacyRibbon);
        end
    end

    -- 处理热加载情况的特殊逻辑
    if m_isInHotload then
        if ms_OtherPlayerID then 
            -- OnTalkToLeader( ms_OtherPlayerID ); 
        end
        m_isInHotload = false;
    end

end

-------------------------------------------------------------------------------
StatementHandlers = {}
-- 为特定语句类型设置默认的处理函数
-- forStatement: 指定的语句类型
function SetDefaultHandlers(forStatement)
    StatementHandlers[forStatement] = {};
    StatementHandlers[forStatement].ExtractStatement = DiplomacySupport_ExtractStatement;
    StatementHandlers[forStatement].ParseStatement = DiplomacySupport_ParseStatement;
    StatementHandlers[forStatement].ParseStatementSelection = DiplomacySupport_ParseStatementSelection;
    StatementHandlers[forStatement].RemoveInvalidSelections = DiplomacySupport_RemoveInvalidSelections;
    StatementHandlers[forStatement].ApplyStatement = ApplyStatement;
    StatementHandlers[forStatement].OnSelectionButtonClicked = OnSelectConversationDiplomacyStatement;
end

-- 设置默认的处理函数
SetDefaultHandlers("DEFAULT");
DefaultHandlers = StatementHandlers["DEFAULT"];

-------------------------------------------------------------------------------
-- 处理建立交易的语句
-- handler: 处理函数的表
-- statementTypeName: 语句的类型名称
-- statementSubTypeName: 语句的子类型名称
-- toPlayer: 语句的目标玩家ID
-- kStatement: 语句的详细信息
function MakeDeal_ApplyStatement(handler : table, statementTypeName : string, statementSubTypeName : string, toPlayer : number, kStatement : table)
    
    -- 判断是初始交易提议还是调整后的交易提议，或者交易已被接受/拒绝
    if ((kStatement.DealAction == DealProposalAction.ADJUSTED and not ms_bShowingDeal) or (statementSubTypeName == "NONE" and (kStatement.ResponseType == DiplomacyResponseTypes.INITIAL or kStatement.ResponseType == DiplomacyResponseTypes.ACKNOWLEDGE or not ms_bShowingDeal))) then

        ApplyStatement(handler, statementTypeName, statementSubTypeName, toPlayer, kStatement);
        -- 隐藏当前屏幕除背景外的所有内容
        Controls.ConversationContainer:SetHide(true);
        Controls.OverviewContainer:SetHide(true);

        LuaEvents.DiploPopup_ShowMakeDeal(ms_OtherPlayerID);
        ms_bShowingDeal = true;
        UI.PlaySound("UI_Diplomacy_Menu_Change");

    elseif (kStatement.RespondingToDealAction ~= DealProposalAction.EQUALIZE and kStatement.RespondingToDealAction ~= DealProposalAction.INSPECT and (statementSubTypeName == "HUMAN_ACCEPT_DEAL" or statementSubTypeName == "HUMAN_REFUSE_DEAL" or statementSubTypeName == "AI_ACCEPT_DEAL" or statementSubTypeName == "AI_REFUSE_DEAL")) then
        -- 从交易屏幕返回
        LuaEvents.DiploPopup_HideDeal();
        ms_bShowingDeal = false;

        if (ms_ActiveSessionID ~= nil) then
            if (ms_OtherPlayerID ~= -1 and Players[ms_OtherPlayerID]:IsHuman()) then
                -- 关闭会话，这将退出回到概览模式
                DiplomacyManager.CloseSession( ms_ActiveSessionID );
            else
                Controls.ConversationContainer:SetHide(false);
                if (ms_currentViewMode ~= CONVERSATION_MODE) then
                    SetConversationMode(ms_SelectedPlayer);
                end

                ApplyStatement(handler, statementTypeName, statementSubTypeName, toPlayer, kStatement);
            end
        else
            -- 由于某些原因没有会话，直接返回概览模式
            Controls.OverviewContainer:SetHide(false);
            SelectPlayer(ms_OtherPlayerID, OVERVIEW_MODE);
        end
    else
        -- 其他操作仅更新交易行动。尤其是来自AI的操作，AI会根据情况自动发送接受、拒绝等消息。
        local eFromPlayerMood = GetStatementMood( kStatement.FromPlayer, kStatement.FromPlayerMood);
        local kParsedStatement = handler.ExtractStatement(handler, statementTypeName, statementSubTypeName, kStatement.FromPlayer, eFromPlayerMood, kStatement.Initiator);

        if (kParsedStatement.LeaderAnimation ~= nil) then
            local bPlay = true;
            -- 检查是否是对等化或检查的响应
            if (kStatement.RespondingToDealAction == DealProposalAction.EQUALIZE or kStatement.RespondingToDealAction == DealProposalAction.INSPECT) then
                -- 不重复播放相同的动画
                if (ms_LastDealResponseAnimation ~= nil and kParsedStatement.LeaderAnimation == ms_LastDealResponseAnimation) then
                    bPlay = false;
                end
            end

            if (bPlay == true) then
                ms_LastDealResponseAnimation = kParsedStatement.LeaderAnimation;
                LeaderSupport_QueueAnimationSequence( ms_SelectedPlayerLeaderTypeName, kParsedStatement.LeaderAnimation );
            end

        end

        local leaderstr = Locale.Lookup( DiplomacyManager.FindTextKey( kParsedStatement.StatementText, kStatement.FromPlayer, kStatement.FromMood, toPlayer));
        LuaEvents.DiploPopup_DealUpdated(ms_OtherPlayerID, kStatement.DealAction, leaderstr);
    end
end

-------------------------------------------------------------------------------
-- 测试交易是否有效的函数
-- 参数 sessionID 会话ID，用于标识特定的外交会话
-- 参数 otherPlayer 另一个玩家，需要验证其是否与会话信息中的玩家匹配
-- 返回 返回一个布尔值，如果交易有效则返回true，否则返回false
function MakeDeal_TestValid(sessionID, otherPlayer)
    
    if (sessionID ~= nil) then
        -- 获取指定sessionID的会话信息
        local sessionInfo = DiplomacyManager.GetSessionInfo(sessionID);
        if (sessionInfo.FromPlayer == otherPlayer) then
            -- 获取当前玩家与指定玩家之间正在商谈的交易
            local pDeal = DealManager.GetWorkingDeal(DealDirection.INCOMING, Game.GetLocalPlayer(), otherPlayer);
            if (pDeal == nil or pDeal:GetItemCount() == 0 or not pDeal:IsValid()) then
                -- 如果交易不存在、交易项目为空或交易无效，则认为会话无效
                return false;
            end
        end
    end
    -- 如果上述条件均满足，则认为交易有效
    return true;

end

-- 设置默认处理程序为MAKE_DEAL
SetDefaultHandlers("MAKE_DEAL");
-- 将MakeDeal_ApplyStatement函数应用于MAKE_DEAL语句的执行
StatementHandlers["MAKE_DEAL"].ApplyStatement = MakeDeal_ApplyStatement;
-- 将MakeDeal_TestValid函数应用于MAKE_DEAL语句的有效性测试
StatementHandlers["MAKE_DEAL"].TestValid = MakeDeal_TestValid;


-------------------------------------------------------------------------------
-- 测试建立交易的需求是否有效
-- sessionID: 会话的ID
-- otherPlayer: 另一个玩家的ID
-- 返回值: 如果需求有效则返回true，否则返回false
function MakeDemand_ApplyStatement(handler : table, statementTypeName : string, statementSubTypeName : string, toPlayer : number, kStatement : table)

    ms_currentViewMode = DEAL_MODE;

    if (statementSubTypeName == "NONE" and (kStatement.ResponseType == DiplomacyResponseTypes.INITIAL or kStatement.ResponseType == DiplomacyResponseTypes.ACKNOWLEDGE or not ms_bShowingDeal)) then

        ApplyStatement(handler, statementTypeName, statementSubTypeName, toPlayer, kStatement);

        -- 隐藏当前屏幕除背景外的所有内容
        Controls.ConversationContainer:SetHide(true);
        Controls.OverviewContainer:SetHide(true);

        LuaEvents.DiploPopup_ShowMakeDemand(ms_OtherPlayerID);
        ms_bShowingDeal = true;
        UI.PlaySound("UI_Diplomacy_Menu_Change");
    elseif (statementSubTypeName == "HUMAN_ACCEPT_DEAL" or statementSubTypeName == "HUMAN_REFUSE_DEAL" or statementSubTypeName == "AI_ACCEPT_DEAL" or statementSubTypeName == "AI_REFUSE_DEAL") then
        
        LuaEvents.DiploPopup_HideDeal();
        ms_bShowingDeal = false;

        if (ms_ActiveSessionID ~= nil) then
            if (ms_OtherPlayerID ~= -1 and Players[ms_OtherPlayerID]:IsHuman()) then
                -- 关闭会话，这将退出回到概览模式
                DiplomacyManager.CloseSession( ms_ActiveSessionID );
            else
                Controls.ConversationContainer:SetHide(false);
                if (ms_currentViewMode ~= CONVERSATION_MODE) then
                    SetConversationMode(ms_SelectedPlayer);
                end

                ApplyStatement(handler, statementTypeName, statementSubTypeName, toPlayer, kStatement);
            end
        else
            -- 由于某些原因没有会话，直接返回概览模式
            Controls.OverviewContainer:SetHide(false);
            SelectPlayer(ms_OtherPlayerID, OVERVIEW_MODE);
        end
    end
end

-- 设置建立需求的默认处理函数
SetDefaultHandlers("MAKE_DEMAND");
StatementHandlers["MAKE_DEMAND"].ApplyStatement = MakeDemand_ApplyStatement;

-- ===========================================================================
-- 当与领导者交谈时触发的函数
-- 参数 playerID : number - 触发该函数的玩家ID
function OnTalkToLeader( playerID : number )
    -- 打开外交行动视图
    OnOpenDiplomacyActionView( playerID );
end

-- ===========================================================================
-- 关闭当前焦点所在的内容；如果是对话或对话框，则它们会收到关闭操作，否则屏幕本身会关闭。
-- 参数:
-- bForced: boolean 值，指示是否强制关闭。
-- ===========================================================================
function CloseFocusedState(bForced : boolean)
    if m_PopupDialog:IsOpen() then -- 检查弹出对话框是否打开，如果打开，则关闭并返回
        m_PopupDialog:Close();
        return;
    end
    if (ms_currentViewMode == CONVERSATION_MODE) then -- 当前视图模式为对话模式
        if (ms_ActiveSessionID ~= nil) then -- 检查是否有活跃的会话
            if (Controls.BlackFadeAnim:IsStopped()) then -- 如果黑色淡入动画停止，则退出对话模式
                ExitConversationMode(bForced);
            end
        else
            Close(); -- 如果没有活跃的会话，直接关闭
        end
    elseif (ms_currentViewMode == CINEMA_MODE) then -- 当前视图模式为电影模式
        UI.PlaySound("Stop_Leader_Speech"); -- 播放停止领导者演讲的声音
        if (Controls.BlackFadeAnim:IsStopped()) then -- 如果黑色淡入动画停止，则开始淡出
            StartFadeOut();
        end
    elseif (ms_currentViewMode == DEAL_MODE) then -- 当前视图模式为交易模式
            -- 在转换到交易模式或从交易模式转换时，不处理ESC键。如果交易屏幕打开，交易屏幕会处理它。
    else
        Close(); -- 其他情况，直接关闭
    end
end

-- ===========================================================================
-- 处理右键按钮事件。
-- 如果当前视图模式为电影模式且黑色淡入动画停止，则关闭当前焦点状态。
function HandleRMB()
    if (ms_currentViewMode == CINEMA_MODE and Controls.BlackFadeAnim:IsStopped()) then
        CloseFocusedState(false);
    end
end


-- ===========================================================================
-- 输入处理
-- 如果此上下文可见，则有机会处理输入。
-- 参数:
-- key: number 表示按键的编号。
-- ===========================================================================
function KeyHandler( key:number )
    if (key == Keys.VK_ESCAPE) then -- 如果按下的是ESC键，则关闭当前焦点状态
        CloseFocusedState(false);
    end    
end
-- ===========================================================================
-- UI回调
-- 消耗所有按键输入，使其不会穿透到世界视图。
-- ===========================================================================
function OnInputHandler( pInputStruct:table )
    local uiMsg = pInputStruct:GetMessageType();
    if uiMsg == KeyEvents.KeyUp then 
        KeyHandler( pInputStruct:GetKey() ); 
        return true;
    elseif uiMsg == KeyEvents.KeyDown then
        return true;
    end

    return false;
end

-- ===========================================================================
-- 外交声明处理
-- 处理来自玩家的外交声明，仅当声明是针对或来自本地玩家时才处理。
-- 参数 fromPlayer 发起声明的玩家编号
-- 参数 toPlayer 声明指向的玩家编号
-- 参数 kVariants 包含声明各种变量的表
function OnDiplomacyStatement(fromPlayer : number, toPlayer : number, kVariants : table)
    
    local localPlayer = Game.GetLocalPlayer();

    -- 检查声明是否与本地玩家有关
    if (toPlayer == localPlayer or fromPlayer == localPlayer) then

        -- 如果没有开启外交能力，忽略声明
        if (not HasCapability("CAPABILITY_DIPLOMACY")) then
            DiplomacyManager.CloseSession( kVariants.SessionID );
            return;
        end

        local statementTypeName = DiplomacyManager.GetKeyName( kVariants.StatementType );
        if (statementTypeName ~= nil) then
            local statementSubTypeName = DiplomacyManager.GetKeyName( kVariants.StatementSubType );

            -- 获取声明处理器
            local handler = GetStatementHandler(statementTypeName);

            -- 初始化会话ID和对方玩家信息
            if (ms_ActiveSessionID == nil) then

                ms_ActiveSessionID = kVariants.SessionID;

                -- 确定对方玩家ID
                if (toPlayer == localPlayer) then
                    ms_OtherPlayerID = fromPlayer;
                else
                    ms_OtherPlayerID = toPlayer;
                end

                -- 获取对方玩家配置信息
                local pOtherPlayerConfig = PlayerConfigurations[ ms_OtherPlayerID ];

                ms_OtherLeaderName = pOtherPlayerConfig:GetLeaderTypeName();
                ms_OtherCivilizationID = pOtherPlayerConfig:GetCivilizationTypeID();
                ms_OtherLeaderID = pOtherPlayerConfig:GetLeaderTypeID();

                -- 检查会话是否有效
                if (handler.TestValid == nil or handler.TestValid(ms_ActiveSessionID, ms_OtherPlayerID)) then
                    -- 初始化视图、设置玩家和填充外交横幅
                    InitializeView();
                    SetupPlayers();
                    PopulateDiplomacyRibbon(ms_DiplomacyRibbon);
                else
                    -- 清除会话ID并关闭会话
                    local sessionID = ms_ActiveSessionID;
                    ms_ActiveSessionID = nil;
                    DiplomacyManager.CloseSession( sessionID );
                    return;
                end
            end

            -- 根据声明类型执行相应操作
            if (statementTypeName == "MAKE_DEAL") then
                SelectPlayer(ms_OtherPlayerID, DEAL_MODE); -- 选择玩家，进入交易模式
            else
                local viewMode = CONVERSATION_MODE;
                -- 根据是否为语音动画调整视图模式
                if UI.IsPlayersLeaderAnimated(ms_OtherPlayerID) then
                    local eFromPlayerMood = GetStatementMood(kVariants.FromPlayer, kVariants.FromPlayerMood);
                    local kParsedStatement = handler.ExtractStatement(handler, statementTypeName, statementSubTypeName, kVariants.FromPlayer, eFromPlayerMood, kVariants.Initiator);
                    for _, voiceoverAnimationName in ipairs(VOICEOVER_SUPPORT) do
                        if (voiceoverAnimationName == kParsedStatement.LeaderAnimation ) then
                            viewMode = CINEMA_MODE;
                            break;
                        end
                    end
                end

                SelectPlayer(ms_OtherPlayerID, viewMode, false, true); -- 选择玩家，进入对话或电影模式
            end

            -- 应用声明效果（如果有处理器）
            if (handler.ApplyStatement ~= nil) then
                handler.ApplyStatement(handler, statementTypeName, statementSubTypeName, toPlayer, kVariants);
                m_isInHotload = false;    -- 标记非热加载状态
            end
        end
    end
end
-- ===========================================================================
-- 重置玩家面板的嵌套菜单的状态。
function ResetPlayerPanel()
    if (ms_PlayerPanel ~= nil) then
        ShowOptionStack(false);
    end
end

-- ===========================================================================
-- 确保关闭！
-- ===========================================================================
-- 关闭外交行动视图，并进行一系列清理工作。
function Close()

    print("Closing Diplomacy Action View. m_eventID: "..tostring(m_eventID));

    -- 如果有弹窗正在显示，则关闭它。
    if m_PopupDialog:IsOpen() then
        UI.DataError("Closing DiplomacyActionView but it's popup dialog was open.");
        m_PopupDialog:Close();
    end

    -- 初始化视图清理工作，并触发场景关闭的Lua事件。
    local isCleanExit:boolean = UninitializeView();
    LuaEvents.DiploScene_SceneClosed();
    
    -- 重置玩家面板。
    ResetPlayerPanel();

    -- 设置本地玩家的声音开关值。
    local localPlayer = Game.GetLocalPlayer();
    if ms_LocalPlayer then
        UI.SetSoundSwitchValue("Game_Location", UI.GetNormalEraSoundSwitchValue(ms_LocalPlayer:GetID()));
    end

    -- 总是停止领袖音乐，以正确恢复游戏音乐...
    UI.PlaySound("Stop_Leader_Music");

    -- 检查是否需要停止模组者文明音乐
    if m_bCurrentMusicIsModder then
        UI.StopModCivLeaderMusic(m_curModderMusic);
    end

    -- 如果模组音乐被暂停，则恢复模组音乐播放。
    if (m_bIsModPaused) then
    -- 如果正在播放的是模组音乐（C++ 会为我们确定这一点），则恢复播放。
        UI.ResumeModCivMusic();
        m_bIsModPaused = false;
    end

    -- 播放退出领袖屏幕的声音，并设置声音状态值。
    UI.PlaySound("Exit_Leader_Screen");
    UI.SetSoundStateValue("Game_Views", "Normal_View");

    -- 如果是干净的退出，则显示游戏内UI。
    if isCleanExit then
        LuaEvents.DiplomacyActionView_ShowIngameUI();
    end
end
-- ===========================================================================
-- 关闭按钮处理器
-- 退出外交视图。
-- ===========================================================================
function OnClose()
    -- 行为类似于按下了ESC键，以便正确地进行清理工作
    CloseFocusedState(false);
end
-- ===========================================================================
-- OnShow 函数
-- 此函数用于显示外交基础弹窗。注意，此函数可能在OnDiplomacyStatement处理器完成一些设置后调用，所以不要重置太多内容，假设OnHide已经正确关闭了所有内容。
function OnShow()
    -- 重置动画到初始状态并开始播放
    Controls.AlphaIn:SetToBeginning();
    Controls.SlideIn:SetToBeginning();
    Controls.AlphaIn:Play();
    Controls.SlideIn:Play();

    -- 设置在淡出完成后不关闭会话
    m_bCloseSessionOnFadeComplete = false;

    -- 重置图标和文本实例
    ms_IconAndTextIM:ResetInstances();

    -- 设置玩家和更新所选玩家信息
    SetupPlayers();
    UpdateSelectedPlayer(true);

    -- 隐藏UI元素并通知Lua事件，场景已打开
    LuaEvents.DiploBasePopup_HideUI(true);
    LuaEvents.DiploScene_SceneOpened(ms_SelectedPlayerID, m_LiteMode);
    
    -- 取消所有城市的选中状态，防止因外交关系变化导致的界面异常
    UI.DeselectAllCities();

    -- 清除当前所有工具提示
    TTManager:ClearCurrent();

    -- 如果处于影院模式，则显示影院模式并开始淡入
    if (m_cinemaMode) then
        ShowCinemaMode();
        StartFadeIn();
    end

end

----------------------------------------------------------------    
-- OnHide 函数
-- 此函数用于隐藏外交基础弹窗，清理相关UI状态和事件监听器。
function OnHide()
    -- 隐藏UI并重置黑色渐变动画
    LuaEvents.DiploBasePopup_HideUI(false);
    Controls.BlackFade:SetHide(true);
    Controls.BlackFadeAnim:SetToBeginning();

    -- 移除游戏核心事件的监听器
    Events.LeaderAnimationComplete.Remove( OnLeaderAnimationComplete );
    Events.LeaderScreenFinishedLoading.Remove( OnLeaderLoaded );

    -- 清空显示的领袖名称
    ms_showingLeaderName = "";

end
-- ===========================================================================
-- 设置按钮选中状态
-- 参数 buttonControl: table 按钮控制对象
-- 参数 isSelected: boolean 指定按钮是否被选中
function SetButtonSelected( buttonControl: table, isSelected : boolean )
    buttonControl:SetSelected(isSelected);
    local textColor = COLOR_BUTTONTEXT_NORMAL;
    local shadowColor = COLOR_BUTTONTEXT_NORMAL_SHADOW;
    -- 根据按钮是否选中，设置文本颜色
    if (isSelected == true) then
        textColor = COLOR_BUTTONTEXT_SELECTED;
        shadowColor = COLOR_BUTTONTEXT_SELECTED_SHADOW;
    end
    -- 设置文本颜色和阴影颜色
    buttonControl:GetTextControl():SetColor(textColor,0);
    buttonControl:GetTextControl():SetColor(shadowColor,1);
end

-- ===========================================================================
-- 强制关闭当前界面
-- 主要用于当地玩家回合结束或界面需要强制关闭时
function OnForceClose()
    if (not ContextPtr:IsHidden()) then
        PopulatePlayerPanel(ms_PlayerPanel, ms_SelectedPlayer);
        -- 判断当前视图模式，决定是直接关闭还是进入无焦点状态
        if (ms_currentViewMode == DEAL_MODE) then
            Close();
        else
            CloseFocusedState(true);
        end
    end
end


-- ===========================================================================
-- 当本地玩家回合结束时的处理逻辑
function OnLocalPlayerTurnEnd()
    g_bIsLocalPlayerTurn = false;
    OnForceClose();
end

-- ===========================================================================
-- 当本地玩家回合开始时的处理逻辑
function OnLocalPlayerTurnBegin()
    g_bIsLocalPlayerTurn = true;
    -- 如果当前界面未隐藏，则执行强制关闭逻辑，确保界面状态正确
    if(not ContextPtr:IsHidden()) then
        OnForceClose();
    end
end

-- ===========================================================================
-- 处理玩家失败事件
-- 如果失败的玩家是本地玩家，则关闭当前界面
-- 参数 player: 玩家ID
-- 参数 defeat: boolean, 是否失败
-- 参数 eventID: number, 事件ID
function OnPlayerDefeat( player, defeat, eventID)
    local localPlayer = Game.GetLocalPlayer();
    -- 检查是否存在本地玩家
    if (localPlayer and localPlayer >= 0) then        
        -- 检查是否是本地玩家失败
        if (localPlayer == player) then
            Close();
        end
    end
end

-- ===========================================================================
-- 处理团队胜利事件
-- 如果获胜团队包含本地玩家，则关闭当前界面
-- 参数 team: number, 团队ID
-- 参数 victory: boolean, 是否胜利
-- 参数 eventID: number, 事件ID
function OnTeamVictory(team, victory, eventID)
    local localPlayer = Game.GetLocalPlayer();
    -- 检查是否存在本地玩家
    if (localPlayer and localPlayer >= 0) then        
        Close();
    end
end

-- ===========================================================================
-- 当有阻塞弹窗显示时的处理逻辑
-- 主要用于确保当前界面状态正确
function OnBlockingPopupShown()
    OnForceClose();   
end

-- ===========================================================================
-- 引擎事件
-- ===========================================================================
-- 当用户请求关闭时触发的函数
function OnUserRequestClose()
    -- 检查当前界面是否显示，且当前视图模式不是交易模式
    -- 如果是，则弹出对话框以处理关闭请求
    if (not ContextPtr:IsHidden() and ms_currentViewMode ~= DEAL_MODE) then
        m_PopupDialog:Reset(); -- 重置对话框
        m_PopupDialog:AddText(Locale.Lookup("LOC_CONFIRM_EXIT_TXT")); -- 添加确认退出的文本
        -- 添加“是”按钮，点击时触发OnQuitYes函数
        m_PopupDialog:AddButton(Locale.Lookup("LOC_YES"), OnQuitYes, nil, nil, "PopupButtonInstanceRed");
        m_PopupDialog:AddButton(Locale.Lookup("LOC_NO"), nil); -- 添加“否”按钮，不设置点击事件
        m_PopupDialog:Open(); -- 打开对话框
    end
end

-- ===========================================================================
-- UI回调
-- ===========================================================================
-- 当用户在确认关闭的对话框中点击“是”时触发的函数
function OnQuitYes()
    Events.UserConfirmedClose(); -- 触发用户确认关闭的事件
end

-- ===========================================================================
-- 热加载UI事件
-- ===========================================================================
-- 初始化函数，负责在界面创建时进行后期初始化和面板创建。
-- 参数 isHotload boolean: 表示是否为热加载。
function OnInit(isHotload:boolean)
    LateInitialize(); -- 进行后期初始化
    CreatePanels(); -- 创建面板
    if isHotload and not ContextPtr:IsHidden() then
        -- 如果是热加载且当前上下文未被隐藏，则获取“外交行动视图”的值
        LuaEvents.GameDebug_GetValues( "DiplomacyActionView" );    
    end
end

-- 上下文析构函数 - 当整个上下文被移除时调用，而不是仅当屏幕被解散！
-- 注释 不会调用当屏幕被解散，仅当整个上下文被移除。
function OnShutdown()
    -- 为热加载缓存值...
    LuaEvents.GameDebug_AddValue("DiplomacyActionView", "isHidden", ContextPtr:IsHidden());
    LuaEvents.GameDebug_AddValue("DiplomacyActionView", "otherPlayerID", ms_OtherPlayerID);
    LuaEvents.GameDebug_AddValue("DiplomacyActionView", "liteMode", m_LiteMode);
end

-- LUA事件：在热加载后设置回缓存的值。
-- 参数 context string: 上下文名称。
-- 参数 contextTable table: 上下文相关的表格信息。
function OnGameDebugReturn( context:string, contextTable:table )
    if context == "DiplomacyActionView" and contextTable["isHidden"] ~= nil and not contextTable["isHidden"] then
        -- 如果上下文为“外交行动视图”且未被隐藏，则恢复相关设置并打开外交行动视图
        m_LiteMode = contextTable["liteMode"];
        OnOpenDiplomacyActionView(contextTable["otherPlayerID"]);
    end
end
-- ===========================================================================
-- 当游戏的暂停状态发生变化时触发的函数
-- 参数:
-- bNewState: 新的暂停状态（布尔值，true表示暂停，false表示未暂停）
function OnGamePauseStateChanged(bNewState)
    if (not ContextPtr:IsHidden()) then
        ResetPlayerPanel(); -- 重置玩家面板
        SelectPlayer(ms_SelectedPlayerID, OVERVIEW_MODE, true); -- 选择玩家并跳转到概览模式
    end
end

-- ===========================================================================
-- 在游戏后期进行初始化的函数
function LateInitialize()
    
    print("PKScreenSizeVal:", UIManager:GetScreenSizeVal())
    -- 注册游戏核心事件监听
    Events.DiplomacySessionClosed.Add( OnDiplomacySessionClosed );
    Events.DiplomacyStatement.Add( OnDiplomacyStatement );
    Events.DiplomacyMakePeace.Add( OnDiplomacyMakePeace );
    Events.LocalPlayerTurnEnd.Add( OnLocalPlayerTurnEnd );
    Events.LocalPlayerTurnBegin.Add( OnLocalPlayerTurnBegin );
    Events.UserRequestClose.Add( OnUserRequestClose );
    Events.GamePauseStateChanged.Add(OnGamePauseStateChanged);
    Events.PlayerDefeat.Add( OnPlayerDefeat );
    Events.TeamVictory.Add( OnTeamVictory );

    -- 注册LUA事件监听
    LuaEvents.CityBannerManager_TalkToLeader.Add(OnTalkToLeader);
    LuaEvents.DiploPopup_TalkToLeader.Add(OnTalkToLeader);
    LuaEvents.DiplomacyRibbon_OpenDiplomacyActionView.Add(OnOpenDiplomacyActionView);
    LuaEvents.TopPanel_OpenDiplomacyActionView.Add(OnOpenDiplomacyActionView);    
    LuaEvents.DiploScene_SetDealAnimation.Add(OnSetDealAnimation);
    LuaEvents.NaturalWonderPopup_Shown.Add(OnBlockingPopupShown);
    LuaEvents.WonderBuiltPopup_Shown.Add(OnBlockingPopupShown);
    LuaEvents.DiplomacyActionView_OpenLite.Add(OnOpenDiplomacyActionViewLite);

    -- 设置关闭按钮的回调事件
    Controls.CloseButton:RegisterCallback( Mouse.eLClick, OnClose );
    Controls.CloseButton:RegisterCallback( Mouse.eMouseEnter, function() UI.PlaySound("Main_Menu_Mouse_Over"); end);
    
    -- 根据屏幕大小调整控件尺寸
    local screenX, screenY:number = UIManager:GetScreenSizeVal();
    local leaderResponseX = math.floor(screenX * CONVO_X_MULTIPLIER);
    Controls.LeaderResponseGrid:SetSizeX(leaderResponseX);
    Controls.LeaderResponseText:SetWrapWidth(leaderResponseX-40);
    Controls.LeaderReasonText:SetWrapWidth(leaderResponseX-40);

    -- 注册右键点击的回调事件
    Controls.ScreenClickRegion:RegisterCallback( Mouse.eRClick, HandleRMB )

    -- 初始化暂停状态标志
    m_bIsModPaused = false;
end

-- ===========================================================================
-- 初始化函数，设置各种处理器
function Initialize()
    ContextPtr:SetInitHandler( OnInit );
    ContextPtr:SetInputHandler( OnInputHandler, true );
    ContextPtr:SetShutdown( OnShutdown );
    ContextPtr:SetShowHandler( OnShow );
    ContextPtr:SetHideHandler( OnHide );
    LuaEvents.GameDebug_Return.Add( OnGameDebugReturn );    
end

-- 仅当游戏支持外交能力时执行初始化
if GameCapabilities.HasCapability("CAPABILITY_DIPLOMACY") then
    Initialize();
end


